From f9b48e065150f9cdc58242a94266a76a85752b02 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 8 Dec 2022 11:28:17 +0100 Subject: [PATCH] vendor: Upgrade dompdf to v2.0.1 --- library/vendor/dompdf/AUTHORS.md | 24 + .../vendor/dompdf/{LICENSE => LICENSE.LGPL} | 0 library/vendor/dompdf/README.md | 232 + library/vendor/dompdf/SOURCE | 24 - library/vendor/dompdf/VERSION | 1 + library/vendor/dompdf/autoload.inc.php | 1 + .../fonts/dompdf_font_family_cache.dist.php | 95 - library/vendor/dompdf/lib/html5lib/Data.php | 123 - .../dompdf/lib/html5lib/InputStream.php | 299 -- library/vendor/dompdf/lib/html5lib/Parser.php | 37 - .../vendor/dompdf/lib/html5lib/Tokenizer.php | 2470 ---------- .../dompdf/lib/html5lib/TreeBuilder.php | 3989 ----------------- .../html5lib/named-character-references.ser | 1 - .../src/Svg/Surface/SurfaceGmagick.php | 308 -- .../dompdf/lib/php-svg-lib/src/autoload.php | 17 - .../vendor/dompdf/lib/res/broken_image.svg | 8 - library/vendor/dompdf/src/Adapter/CPDF.php | 1184 ----- library/vendor/dompdf/src/Autoloader.php | 42 - library/vendor/dompdf/src/Canvas.php | 422 -- library/vendor/dompdf/src/Css/Style.php | 2952 ------------ library/vendor/dompdf/src/FontMetrics.php | 538 --- library/vendor/dompdf/src/Frame/FrameList.php | 35 - .../dompdf/src/Frame/FrameListIterator.php | 91 - .../vendor/dompdf/src/Frame/FrameTreeList.php | 35 - .../dompdf/src/FrameDecorator/Block.php | 284 -- .../dompdf/src/FrameDecorator/Inline.php | 106 - .../dompdf/src/FrameDecorator/ListBullet.php | 87 - .../src/FrameDecorator/ListBulletImage.php | 154 - .../dompdf/src/FrameDecorator/Table.php | 398 -- .../dompdf/src/FrameDecorator/TableRow.php | 68 - .../FrameReflower/AbstractFrameReflower.php | 529 --- .../vendor/dompdf/src/FrameReflower/Block.php | 948 ---- .../vendor/dompdf/src/FrameReflower/Image.php | 206 - .../dompdf/src/FrameReflower/Inline.php | 103 - .../vendor/dompdf/src/FrameReflower/Table.php | 589 --- .../src/FrameReflower/TableRowGroup.php | 72 - .../vendor/dompdf/src/FrameReflower/Text.php | 511 --- library/vendor/dompdf/src/Image/Cache.php | 186 - .../vendor/dompdf/src/Positioner/Absolute.php | 118 - .../vendor/dompdf/src/Positioner/Fixed.php | 89 - .../vendor/dompdf/src/Positioner/Inline.php | 77 - .../dompdf/src/Positioner/ListBullet.php | 76 - .../dompdf/src/Renderer/AbstractRenderer.php | 923 ---- library/vendor/dompdf/src/Renderer/Block.php | 262 -- library/vendor/dompdf/src/Renderer/Image.php | 139 - library/vendor/dompdf/src/Renderer/Inline.php | 211 - .../dompdf/src/Renderer/TableRowGroup.php | 50 - library/vendor/dompdf/vendor/autoload.php | 7 + .../dompdf/vendor/composer/ClassLoader.php | 572 +++ .../vendor/composer/InstalledVersions.php | 350 ++ library/vendor/dompdf/vendor/composer/LICENSE | 21 + .../vendor/composer/autoload_classmap.php | 11 + .../vendor/composer/autoload_namespaces.php | 9 + .../dompdf/vendor/composer/autoload_psr4.php | 14 + .../dompdf/vendor/composer/autoload_real.php | 57 + .../vendor/composer/autoload_static.php | 66 + .../dompdf/vendor/composer/installed.json | 295 ++ .../dompdf/vendor/composer/installed.php | 68 + .../dompdf/vendor/composer/platform_check.php | 26 + .../dompdf/vendor/dompdf/dompdf/AUTHORS.md | 24 + .../dompdf/vendor/dompdf/dompdf/LICENSE.LGPL | 456 ++ .../dompdf/vendor/dompdf/dompdf/README.md | 232 + .../dompdf/vendor/dompdf/dompdf/VERSION | 1 + .../dompdf/vendor/dompdf/dompdf/composer.json | 47 + .../dompdf/dompdf/lib/Cpdf.php} | 3441 ++++++++++---- .../dompdf/dompdf}/lib/fonts/Courier-Bold.afm | 0 .../dompdf}/lib/fonts/Courier-BoldOblique.afm | 0 .../dompdf}/lib/fonts/Courier-Oblique.afm | 0 .../dompdf/dompdf}/lib/fonts/Courier.afm | 0 .../dompdf}/lib/fonts/DejaVuSans-Bold.ttf | Bin .../dompdf}/lib/fonts/DejaVuSans-Bold.ufm | 0 .../lib/fonts/DejaVuSans-BoldOblique.ttf | Bin .../lib/fonts/DejaVuSans-BoldOblique.ufm | 0 .../dompdf}/lib/fonts/DejaVuSans-Oblique.ttf | Bin .../dompdf}/lib/fonts/DejaVuSans-Oblique.ufm | 0 .../dompdf/dompdf}/lib/fonts/DejaVuSans.ttf | Bin .../dompdf/dompdf}/lib/fonts/DejaVuSans.ufm | 0 .../dompdf}/lib/fonts/DejaVuSansMono-Bold.ttf | Bin .../dompdf}/lib/fonts/DejaVuSansMono-Bold.ufm | 0 .../lib/fonts/DejaVuSansMono-BoldOblique.ttf | Bin .../lib/fonts/DejaVuSansMono-BoldOblique.ufm | 0 .../lib/fonts/DejaVuSansMono-Oblique.ttf | Bin .../lib/fonts/DejaVuSansMono-Oblique.ufm | 0 .../dompdf}/lib/fonts/DejaVuSansMono.ttf | Bin .../dompdf}/lib/fonts/DejaVuSansMono.ufm | 0 .../dompdf}/lib/fonts/DejaVuSerif-Bold.ttf | Bin .../dompdf}/lib/fonts/DejaVuSerif-Bold.ufm | 0 .../lib/fonts/DejaVuSerif-BoldItalic.ttf | Bin .../lib/fonts/DejaVuSerif-BoldItalic.ufm | 0 .../dompdf}/lib/fonts/DejaVuSerif-Italic.ttf | Bin .../dompdf}/lib/fonts/DejaVuSerif-Italic.ufm | 0 .../dompdf/dompdf}/lib/fonts/DejaVuSerif.ttf | Bin .../dompdf/dompdf}/lib/fonts/DejaVuSerif.ufm | 0 .../dompdf}/lib/fonts/Helvetica-Bold.afm | 0 .../lib/fonts/Helvetica-BoldOblique.afm | 0 .../dompdf}/lib/fonts/Helvetica-Oblique.afm | 0 .../dompdf/dompdf}/lib/fonts/Helvetica.afm | 0 .../dompdf/dompdf}/lib/fonts/Symbol.afm | 0 .../dompdf/dompdf}/lib/fonts/Times-Bold.afm | 0 .../dompdf}/lib/fonts/Times-BoldItalic.afm | 0 .../dompdf/dompdf}/lib/fonts/Times-Italic.afm | 0 .../dompdf/dompdf}/lib/fonts/Times-Roman.afm | 0 .../dompdf/dompdf}/lib/fonts/ZapfDingbats.afm | 0 .../lib/fonts/installed-fonts.dist.json | 80 + .../dompdf/dompdf}/lib/fonts/mustRead.html | 0 .../dompdf/dompdf}/lib/res/broken_image.png | Bin .../dompdf/dompdf/lib/res/broken_image.svg | 8 + .../dompdf/dompdf}/lib/res/html.css | 27 +- .../vendor/dompdf/dompdf/src/Adapter/CPDF.php | 944 ++++ .../dompdf/dompdf}/src/Adapter/GD.php | 611 +-- .../dompdf/dompdf}/src/Adapter/PDFLib.php | 666 ++- .../vendor/dompdf/dompdf/src/Canvas.php | 477 ++ .../dompdf/dompdf}/src/CanvasFactory.php | 3 +- .../dompdf/dompdf}/src/Cellmap.php | 469 +- .../dompdf}/src/Css/AttributeTranslator.php | 287 +- .../dompdf/dompdf}/src/Css/Color.php | 128 +- .../vendor/dompdf/dompdf/src/Css/Style.php | 3743 ++++++++++++++++ .../dompdf/dompdf}/src/Css/Stylesheet.php | 412 +- .../{ => vendor/dompdf/dompdf}/src/Dompdf.php | 632 ++- .../dompdf/dompdf}/src/Exception.php | 4 +- .../dompdf}/src/Exception/ImageException.php | 3 +- .../vendor/dompdf/dompdf/src/FontMetrics.php | 635 +++ .../{ => vendor/dompdf/dompdf}/src/Frame.php | 497 +- .../dompdf/dompdf}/src/Frame/Factory.php | 38 +- .../dompdf/src/Frame/FrameListIterator.php | 100 + .../dompdf/dompdf}/src/Frame/FrameTree.php | 43 +- .../dompdf}/src/Frame/FrameTreeIterator.php | 36 +- .../FrameDecorator/AbstractFrameDecorator.php | 478 +- .../dompdf/src/FrameDecorator/Block.php | 256 ++ .../dompdf}/src/FrameDecorator/Image.php | 41 +- .../dompdf/src/FrameDecorator/Inline.php | 121 + .../dompdf/src/FrameDecorator/ListBullet.php | 117 + .../src/FrameDecorator/ListBulletImage.php | 112 + .../src/FrameDecorator/NullFrameDecorator.php | 3 +- .../dompdf}/src/FrameDecorator/Page.php | 366 +- .../dompdf/src/FrameDecorator/Table.php | 343 ++ .../dompdf}/src/FrameDecorator/TableCell.php | 13 +- .../dompdf/src/FrameDecorator/TableRow.php | 28 + .../src/FrameDecorator/TableRowGroup.php | 32 +- .../dompdf}/src/FrameDecorator/Text.php | 104 +- .../FrameReflower/AbstractFrameReflower.php | 705 +++ .../dompdf/dompdf/src/FrameReflower/Block.php | 949 ++++ .../dompdf/dompdf/src/FrameReflower/Image.php | 213 + .../dompdf/src/FrameReflower/Inline.php | 188 + .../dompdf}/src/FrameReflower/ListBullet.php | 22 +- .../src/FrameReflower/NullFrameReflower.php | 4 +- .../dompdf/dompdf}/src/FrameReflower/Page.php | 58 +- .../dompdf/dompdf/src/FrameReflower/Table.php | 523 +++ .../dompdf}/src/FrameReflower/TableCell.php | 78 +- .../dompdf}/src/FrameReflower/TableRow.php | 28 +- .../src/FrameReflower/TableRowGroup.php | 71 + .../dompdf/dompdf/src/FrameReflower/Text.php | 605 +++ .../dompdf/dompdf}/src/Helpers.php | 454 +- .../vendor/dompdf/dompdf/src/Image/Cache.php | 254 ++ .../dompdf/dompdf}/src/JavascriptEmbedder.php | 3 +- .../dompdf/dompdf}/src/LineBox.php | 159 +- .../dompdf/dompdf}/src/Options.php | 324 +- .../dompdf/dompdf}/src/PhpEvaluator.php | 5 +- .../dompdf/dompdf/src/Positioner/Absolute.php | 128 + .../src/Positioner/AbstractPositioner.php | 26 +- .../dompdf/dompdf}/src/Positioner/Block.php | 22 +- .../dompdf/dompdf/src/Positioner/Fixed.php | 92 + .../dompdf/dompdf/src/Positioner/Inline.php | 52 + .../dompdf/src/Positioner/ListBullet.php | 42 + .../dompdf}/src/Positioner/NullPositioner.php | 6 +- .../dompdf}/src/Positioner/TableCell.php | 6 +- .../dompdf}/src/Positioner/TableRow.php | 6 +- .../dompdf/dompdf}/src/Renderer.php | 58 +- .../dompdf/src/Renderer/AbstractRenderer.php | 1244 +++++ .../dompdf/dompdf/src/Renderer/Block.php | 88 + .../dompdf/dompdf/src/Renderer/Image.php | 90 + .../dompdf/dompdf/src/Renderer/Inline.php | 126 + .../dompdf}/src/Renderer/ListBullet.php | 96 +- .../dompdf/dompdf}/src/Renderer/TableCell.php | 117 +- .../dompdf/src/Renderer/TableRowGroup.php | 40 + .../dompdf/dompdf}/src/Renderer/Text.php | 41 +- .../dompdf/vendor/masterminds/html5/CREDITS | 11 + .../vendor/masterminds/html5/LICENSE.txt | 66 + .../dompdf/vendor/masterminds/html5/README.md | 270 ++ .../vendor/masterminds/html5/RELEASE.md | 157 + .../vendor/masterminds/html5/UPGRADING.md | 21 + .../vendor/masterminds/html5/bin/entities.php | 26 + .../vendor/masterminds/html5/composer.json | 42 + .../vendor/masterminds/html5/src/HTML5.php | 246 + .../masterminds/html5/src/HTML5/Elements.php | 619 +++ .../masterminds/html5/src/HTML5/Entities.php | 2236 +++++++++ .../masterminds/html5/src/HTML5/Exception.php | 10 + .../html5/src/HTML5/InstructionProcessor.php | 41 + .../src/HTML5/Parser/CharacterReference.php | 61 + .../html5/src/HTML5/Parser/DOMTreeBuilder.php | 705 +++ .../html5/src/HTML5/Parser/EventHandler.php | 114 + .../src/HTML5/Parser/FileInputStream.php | 33 + .../html5/src/HTML5/Parser/InputStream.php | 87 + .../html5/src/HTML5/Parser/ParseError.php | 10 + .../html5/src/HTML5/Parser/README.md | 53 + .../html5/src/HTML5/Parser/Scanner.php | 416 ++ .../src/HTML5/Parser/StringInputStream.php | 331 ++ .../html5/src/HTML5/Parser/Tokenizer.php | 1197 +++++ .../src/HTML5/Parser/TreeBuildingRules.php | 127 + .../html5/src/HTML5/Parser/UTF8Utils.php | 183 + .../src/HTML5/Serializer/HTML5Entities.php | 1533 +++++++ .../src/HTML5/Serializer/OutputRules.php | 553 +++ .../html5/src/HTML5/Serializer/README.md | 33 + .../src/HTML5/Serializer/RulesInterface.php | 99 + .../html5/src/HTML5/Serializer/Traverser.php | 142 + .../.github/workflows/phpunit.yml | 44 + .../phenx}/php-font-lib/LICENSE | 0 .../vendor/phenx/php-font-lib/README.md | 28 + .../vendor/phenx/php-font-lib/bower.json | 23 + .../vendor/phenx/php-font-lib/composer.json | 32 + .../vendor/phenx/php-font-lib/index.php | 1 + .../maps/adobe-standard-encoding.map | 231 + .../vendor/phenx/php-font-lib/maps/cp1250.map | 251 ++ .../vendor/phenx/php-font-lib/maps/cp1251.map | 255 ++ .../vendor/phenx/php-font-lib/maps/cp1252.map | 251 ++ .../vendor/phenx/php-font-lib/maps/cp1253.map | 239 + .../vendor/phenx/php-font-lib/maps/cp1254.map | 249 + .../vendor/phenx/php-font-lib/maps/cp1255.map | 233 + .../vendor/phenx/php-font-lib/maps/cp1257.map | 244 + .../vendor/phenx/php-font-lib/maps/cp1258.map | 247 + .../vendor/phenx/php-font-lib/maps/cp874.map | 225 + .../phenx/php-font-lib/maps/iso-8859-1.map | 256 ++ .../phenx/php-font-lib/maps/iso-8859-11.map | 248 + .../phenx/php-font-lib/maps/iso-8859-15.map | 256 ++ .../phenx/php-font-lib/maps/iso-8859-16.map | 256 ++ .../phenx/php-font-lib/maps/iso-8859-2.map | 256 ++ .../phenx/php-font-lib/maps/iso-8859-4.map | 256 ++ .../phenx/php-font-lib/maps/iso-8859-5.map | 256 ++ .../phenx/php-font-lib/maps/iso-8859-7.map | 250 ++ .../phenx/php-font-lib/maps/iso-8859-9.map | 256 ++ .../vendor/phenx/php-font-lib/maps/koi8-r.map | 256 ++ .../vendor/phenx/php-font-lib/maps/koi8-u.map | 256 ++ .../src/FontLib/AdobeFontMetrics.php | 4 +- .../php-font-lib/src/FontLib/Autoloader.php | 0 .../php-font-lib/src/FontLib/BinaryStream.php | 11 +- .../php-font-lib/src/FontLib/EOT/File.php | 19 +- .../php-font-lib/src/FontLib/EOT/Header.php | 0 .../php-font-lib/src/FontLib/EncodingMap.php | 0 .../Exception/FontNotFoundException.php | 0 .../phenx}/php-font-lib/src/FontLib/Font.php | 2 +- .../src/FontLib/Glyph/Outline.php | 11 +- .../src/FontLib/Glyph/OutlineComponent.php | 0 .../src/FontLib/Glyph/OutlineComposite.php | 0 .../src/FontLib/Glyph/OutlineSimple.php | 4 +- .../php-font-lib/src/FontLib/Header.php | 0 .../src/FontLib/OpenType/File.php | 0 .../FontLib/OpenType/TableDirectoryEntry.php | 0 .../src/FontLib/Table/DirectoryEntry.php | 9 +- .../php-font-lib/src/FontLib/Table/Table.php | 0 .../src/FontLib/Table/Type/cmap.php | 0 .../src/FontLib/Table/Type/glyf.php | 0 .../src/FontLib/Table/Type/head.php | 0 .../src/FontLib/Table/Type/hhea.php | 0 .../src/FontLib/Table/Type/hmtx.php | 0 .../src/FontLib/Table/Type/kern.php | 0 .../src/FontLib/Table/Type/loca.php | 0 .../src/FontLib/Table/Type/maxp.php | 0 .../src/FontLib/Table/Type/name.php | 0 .../src/FontLib/Table/Type/nameRecord.php | 0 .../src/FontLib/Table/Type/os2.php | 0 .../src/FontLib/Table/Type/post.php | 4 +- .../src/FontLib/TrueType/Collection.php | 0 .../src/FontLib/TrueType/File.php | 2 +- .../src/FontLib/TrueType/Header.php | 0 .../FontLib/TrueType/TableDirectoryEntry.php | 0 .../php-font-lib/src/FontLib/WOFF/File.php | 4 +- .../php-font-lib/src/FontLib/WOFF/Header.php | 0 .../src/FontLib/WOFF/TableDirectoryEntry.php | 0 .../dompdf/vendor/phenx/php-svg-lib/LICENSE | 165 + .../dompdf/vendor/phenx/php-svg-lib/README.md | 13 + .../vendor/phenx/php-svg-lib/composer.json | 31 + .../phenx/php-svg-lib/src/Svg/CssLength.php | 135 + .../php-svg-lib/src/Svg/DefaultStyle.php | 8 +- .../phenx}/php-svg-lib/src/Svg/Document.php | 14 +- .../php-svg-lib/src/Svg/Gradient/Stop.php | 4 +- .../phenx}/php-svg-lib/src/Svg/Style.php | 125 +- .../php-svg-lib/src/Svg/Surface/CPdf.php} | 1922 +++++--- .../src/Svg/Surface/SurfaceCpdf.php | 95 +- .../src/Svg/Surface/SurfaceInterface.php | 8 +- .../src/Svg/Surface/SurfacePDFLib.php | 26 +- .../php-svg-lib/src/Svg/Tag/AbstractTag.php | 79 +- .../phenx}/php-svg-lib/src/Svg/Tag/Anchor.php | 4 +- .../phenx}/php-svg-lib/src/Svg/Tag/Circle.php | 15 +- .../php-svg-lib/src/Svg/Tag/ClipPath.php | 4 +- .../php-svg-lib/src/Svg/Tag/Ellipse.php | 17 +- .../phenx}/php-svg-lib/src/Svg/Tag/Group.php | 4 +- .../phenx}/php-svg-lib/src/Svg/Tag/Image.php | 24 +- .../phenx}/php-svg-lib/src/Svg/Tag/Line.php | 17 +- .../src/Svg/Tag/LinearGradient.php | 4 +- .../phenx}/php-svg-lib/src/Svg/Tag/Path.php | 116 +- .../php-svg-lib/src/Svg/Tag/Polygon.php | 15 +- .../php-svg-lib/src/Svg/Tag/Polyline.php | 15 +- .../src/Svg/Tag/RadialGradient.php | 4 +- .../phenx}/php-svg-lib/src/Svg/Tag/Rect.php | 17 +- .../phenx}/php-svg-lib/src/Svg/Tag/Shape.php | 4 +- .../phenx}/php-svg-lib/src/Svg/Tag/Stop.php | 4 +- .../php-svg-lib/src/Svg/Tag/StyleTag.php | 4 +- .../phenx}/php-svg-lib/src/Svg/Tag/Text.php | 10 +- .../phenx}/php-svg-lib/src/Svg/Tag/UseTag.php | 18 +- .../sabberworm/php-css-parser/CHANGELOG.md | 241 + .../vendor/sabberworm/php-css-parser/LICENSE | 21 + .../sabberworm/php-css-parser/README.md | 632 +++ .../sabberworm/php-css-parser/composer.json | 69 + .../src/CSSList/AtRuleBlockList.php | 83 + .../src/CSSList/CSSBlockList.php | 143 + .../php-css-parser/src/CSSList/CSSList.php | 479 ++ .../php-css-parser/src/CSSList/Document.php | 172 + .../php-css-parser/src/CSSList/KeyFrame.php | 104 + .../php-css-parser/src/Comment/Comment.php | 71 + .../src/Comment/Commentable.php | 25 + .../php-css-parser/src/OutputFormat.php | 334 ++ .../php-css-parser/src/OutputFormatter.php | 231 + .../sabberworm/php-css-parser/src/Parser.php | 60 + .../src/Parsing/OutputException.php | 18 + .../src/Parsing/ParserState.php | 516 +++ .../src/Parsing/SourceException.php | 32 + .../src/Parsing/UnexpectedEOFException.php | 12 + .../src/Parsing/UnexpectedTokenException.php | 51 + .../php-css-parser/src/Property/AtRule.php | 34 + .../src/Property/CSSNamespace.php | 154 + .../php-css-parser/src/Property/Charset.php | 129 + .../php-css-parser/src/Property/Import.php | 137 + .../src/Property/KeyframeSelector.php | 23 + .../php-css-parser/src/Property/Selector.php | 138 + .../php-css-parser/src/Renderable.php | 21 + .../php-css-parser/src/Rule/Rule.php | 392 ++ .../php-css-parser/src/RuleSet/AtRuleSet.php | 73 + .../src/RuleSet/DeclarationBlock.php | 831 ++++ .../php-css-parser/src/RuleSet/RuleSet.php | 326 ++ .../php-css-parser/src/Settings.php | 89 + .../php-css-parser/src/Value/CSSFunction.php | 73 + .../php-css-parser/src/Value/CSSString.php | 105 + .../php-css-parser/src/Value/CalcFunction.php | 89 + .../src/Value/CalcRuleValueList.php | 24 + .../php-css-parser/src/Value/Color.php | 166 + .../php-css-parser/src/Value/LineName.php | 65 + .../src/Value/PrimitiveValue.php | 14 + .../src/Value/RuleValueList.php | 15 + .../php-css-parser/src/Value/Size.php | 209 + .../php-css-parser/src/Value/URL.php | 82 + .../php-css-parser/src/Value/Value.php | 198 + .../php-css-parser/src/Value/ValueList.php | 100 + 342 files changed, 44506 insertions(+), 23634 deletions(-) create mode 100644 library/vendor/dompdf/AUTHORS.md rename library/vendor/dompdf/{LICENSE => LICENSE.LGPL} (100%) create mode 100644 library/vendor/dompdf/README.md delete mode 100644 library/vendor/dompdf/SOURCE create mode 100644 library/vendor/dompdf/VERSION create mode 100644 library/vendor/dompdf/autoload.inc.php delete mode 100644 library/vendor/dompdf/lib/fonts/dompdf_font_family_cache.dist.php delete mode 100644 library/vendor/dompdf/lib/html5lib/Data.php delete mode 100644 library/vendor/dompdf/lib/html5lib/InputStream.php delete mode 100644 library/vendor/dompdf/lib/html5lib/Parser.php delete mode 100644 library/vendor/dompdf/lib/html5lib/Tokenizer.php delete mode 100644 library/vendor/dompdf/lib/html5lib/TreeBuilder.php delete mode 100644 library/vendor/dompdf/lib/html5lib/named-character-references.ser delete mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php delete mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/autoload.php delete mode 100644 library/vendor/dompdf/lib/res/broken_image.svg delete mode 100644 library/vendor/dompdf/src/Adapter/CPDF.php delete mode 100644 library/vendor/dompdf/src/Autoloader.php delete mode 100644 library/vendor/dompdf/src/Canvas.php delete mode 100644 library/vendor/dompdf/src/Css/Style.php delete mode 100644 library/vendor/dompdf/src/FontMetrics.php delete mode 100644 library/vendor/dompdf/src/Frame/FrameList.php delete mode 100644 library/vendor/dompdf/src/Frame/FrameListIterator.php delete mode 100644 library/vendor/dompdf/src/Frame/FrameTreeList.php delete mode 100644 library/vendor/dompdf/src/FrameDecorator/Block.php delete mode 100644 library/vendor/dompdf/src/FrameDecorator/Inline.php delete mode 100644 library/vendor/dompdf/src/FrameDecorator/ListBullet.php delete mode 100644 library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php delete mode 100644 library/vendor/dompdf/src/FrameDecorator/Table.php delete mode 100644 library/vendor/dompdf/src/FrameDecorator/TableRow.php delete mode 100644 library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php delete mode 100644 library/vendor/dompdf/src/FrameReflower/Block.php delete mode 100644 library/vendor/dompdf/src/FrameReflower/Image.php delete mode 100644 library/vendor/dompdf/src/FrameReflower/Inline.php delete mode 100644 library/vendor/dompdf/src/FrameReflower/Table.php delete mode 100644 library/vendor/dompdf/src/FrameReflower/TableRowGroup.php delete mode 100644 library/vendor/dompdf/src/FrameReflower/Text.php delete mode 100644 library/vendor/dompdf/src/Image/Cache.php delete mode 100644 library/vendor/dompdf/src/Positioner/Absolute.php delete mode 100644 library/vendor/dompdf/src/Positioner/Fixed.php delete mode 100644 library/vendor/dompdf/src/Positioner/Inline.php delete mode 100644 library/vendor/dompdf/src/Positioner/ListBullet.php delete mode 100644 library/vendor/dompdf/src/Renderer/AbstractRenderer.php delete mode 100644 library/vendor/dompdf/src/Renderer/Block.php delete mode 100644 library/vendor/dompdf/src/Renderer/Image.php delete mode 100644 library/vendor/dompdf/src/Renderer/Inline.php delete mode 100644 library/vendor/dompdf/src/Renderer/TableRowGroup.php create mode 100644 library/vendor/dompdf/vendor/autoload.php create mode 100644 library/vendor/dompdf/vendor/composer/ClassLoader.php create mode 100644 library/vendor/dompdf/vendor/composer/InstalledVersions.php create mode 100644 library/vendor/dompdf/vendor/composer/LICENSE create mode 100644 library/vendor/dompdf/vendor/composer/autoload_classmap.php create mode 100644 library/vendor/dompdf/vendor/composer/autoload_namespaces.php create mode 100644 library/vendor/dompdf/vendor/composer/autoload_psr4.php create mode 100644 library/vendor/dompdf/vendor/composer/autoload_real.php create mode 100644 library/vendor/dompdf/vendor/composer/autoload_static.php create mode 100644 library/vendor/dompdf/vendor/composer/installed.json create mode 100644 library/vendor/dompdf/vendor/composer/installed.php create mode 100644 library/vendor/dompdf/vendor/composer/platform_check.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/AUTHORS.md create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/LICENSE.LGPL create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/README.md create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/VERSION create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/composer.json rename library/vendor/dompdf/{lib/php-svg-lib/src/Svg/Surface/CPdf.php => vendor/dompdf/dompdf/lib/Cpdf.php} (60%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Courier-Bold.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Courier-BoldOblique.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Courier-Oblique.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Courier.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans-Bold.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans-Bold.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans-BoldOblique.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans-BoldOblique.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans-Oblique.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans-Oblique.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSans.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono-Bold.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono-Bold.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono-BoldOblique.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono-BoldOblique.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono-Oblique.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono-Oblique.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSansMono.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif-Bold.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif-Bold.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif-BoldItalic.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif-BoldItalic.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif-Italic.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif-Italic.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif.ttf (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/DejaVuSerif.ufm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Helvetica-Bold.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Helvetica-BoldOblique.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Helvetica-Oblique.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Helvetica.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Symbol.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Times-Bold.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Times-BoldItalic.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Times-Italic.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/Times-Roman.afm (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/ZapfDingbats.afm (100%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/fonts/mustRead.html (100%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/res/broken_image.png (100%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.svg rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/lib/res/html.css (94%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/CPDF.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Adapter/GD.php (58%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Adapter/PDFLib.php (72%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Canvas.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/CanvasFactory.php (93%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Cellmap.php (61%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Css/AttributeTranslator.php (74%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Css/Color.php (69%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Style.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Css/Stylesheet.php (83%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Dompdf.php (66%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Exception.php (84%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Exception/ImageException.php (84%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FontMetrics.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Frame.php (76%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Frame/Factory.php (85%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameListIterator.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Frame/FrameTree.php (92%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Frame/FrameTreeIterator.php (70%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameDecorator/AbstractFrameDecorator.php (65%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Block.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameDecorator/Image.php (62%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Inline.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBulletImage.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameDecorator/NullFrameDecorator.php (87%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameDecorator/Page.php (62%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Table.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameDecorator/TableCell.php (92%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameDecorator/TableRowGroup.php (60%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameDecorator/Text.php (58%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Block.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Image.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Inline.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameReflower/ListBullet.php (53%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameReflower/NullFrameReflower.php (87%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameReflower/Page.php (76%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Table.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameReflower/TableCell.php (55%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/FrameReflower/TableRow.php (72%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Text.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Helpers.php (66%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Image/Cache.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/JavascriptEmbedder.php (90%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/LineBox.php (64%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Options.php (76%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/PhpEvaluator.php (89%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Absolute.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Positioner/AbstractPositioner.php (57%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Positioner/Block.php (52%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Fixed.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Inline.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/ListBullet.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Positioner/NullPositioner.php (72%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Positioner/TableCell.php (79%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Positioner/TableRow.php (81%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Renderer.php (82%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/AbstractRenderer.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Block.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Image.php create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Inline.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Renderer/ListBullet.php (63%) rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Renderer/TableCell.php (66%) create mode 100644 library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableRowGroup.php rename library/vendor/dompdf/{ => vendor/dompdf/dompdf}/src/Renderer/Text.php (80%) create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/CREDITS create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/LICENSE.txt create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/README.md create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/RELEASE.md create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/UPGRADING.md create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/bin/entities.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/composer.json create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Elements.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Entities.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Exception.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/InstructionProcessor.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/CharacterReference.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/DOMTreeBuilder.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/EventHandler.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/FileInputStream.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/InputStream.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/ParseError.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/README.md create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Scanner.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/StringInputStream.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Tokenizer.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/UTF8Utils.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/OutputRules.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/README.md create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/RulesInterface.php create mode 100644 library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/Traverser.php create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/.github/workflows/phpunit.yml rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/LICENSE (100%) create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/README.md create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/bower.json create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/composer.json create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/index.php create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/adobe-standard-encoding.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1250.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1251.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1252.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1253.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1254.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1255.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1257.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1258.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp874.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-1.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-11.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-15.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-16.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-2.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-4.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-5.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-7.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-9.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-r.map create mode 100644 library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-u.map rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/AdobeFontMetrics.php (98%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Autoloader.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/BinaryStream.php (97%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/EOT/File.php (93%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/EOT/Header.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/EncodingMap.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Exception/FontNotFoundException.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Font.php (96%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Glyph/Outline.php (93%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Glyph/OutlineComponent.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Glyph/OutlineComposite.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Glyph/OutlineSimple.php (99%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Header.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/OpenType/File.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/DirectoryEntry.php (94%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Table.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/cmap.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/glyf.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/head.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/hhea.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/hmtx.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/kern.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/loca.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/maxp.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/name.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/nameRecord.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/os2.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/Table/Type/post.php (95%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/TrueType/Collection.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/TrueType/File.php (99%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/TrueType/Header.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/WOFF/File.php (95%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/WOFF/Header.php (100%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php (100%) create mode 100644 library/vendor/dompdf/vendor/phenx/php-svg-lib/LICENSE create mode 100644 library/vendor/dompdf/vendor/phenx/php-svg-lib/README.md create mode 100644 library/vendor/dompdf/vendor/phenx/php-svg-lib/composer.json create mode 100644 library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/CssLength.php rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/DefaultStyle.php (82%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Document.php (97%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Gradient/Stop.php (81%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Style.php (88%) rename library/vendor/dompdf/{lib/Cpdf.php => vendor/phenx/php-svg-lib/src/Svg/Surface/CPdf.php} (77%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php (87%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Surface/SurfaceInterface.php (92%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php (94%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/AbstractTag.php (57%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Anchor.php (77%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Circle.php (54%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/ClipPath.php (91%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Ellipse.php (60%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Group.php (91%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Image.php (60%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Line.php (60%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/LinearGradient.php (97%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Path.php (86%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Polygon.php (70%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Polyline.php (69%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/RadialGradient.php (82%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Rect.php (66%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Shape.php (94%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Stop.php (81%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/StyleTag.php (89%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/Text.php (83%) rename library/vendor/dompdf/{lib => vendor/phenx}/php-svg-lib/src/Svg/Tag/UseTag.php (75%) create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/CHANGELOG.md create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/LICENSE create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/README.md create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/composer.json create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/Document.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Comment.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormat.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormatter.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parser.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/ParserState.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedTokenException.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/AtRule.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/CSSNamespace.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Charset.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Import.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Selector.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Renderable.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Rule/Rule.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Settings.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSString.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Color.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/LineName.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Size.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/URL.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Value.php create mode 100644 library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/ValueList.php diff --git a/library/vendor/dompdf/AUTHORS.md b/library/vendor/dompdf/AUTHORS.md new file mode 100644 index 000000000..686147928 --- /dev/null +++ b/library/vendor/dompdf/AUTHORS.md @@ -0,0 +1,24 @@ +Dompdf was designed and developed by Benj Carson. + +### Current Team + +* **Brian Sweeney** (maintainer) +* **Till Berger** + +### Alumni + +* **Benj Carson** (creator) +* **Fabien Ménager** +* **Simon Berger** +* **Orion Richardson** + +### Contributors +* **Gabriel Bull** +* **Barry vd. Heuvel** +* **Ryan H. Masten** +* **Helmut Tischer** +* [and many more...](https://github.com/dompdf/dompdf/graphs/contributors) + +### Thanks + +Dompdf would not have been possible without strong community support. diff --git a/library/vendor/dompdf/LICENSE b/library/vendor/dompdf/LICENSE.LGPL similarity index 100% rename from library/vendor/dompdf/LICENSE rename to library/vendor/dompdf/LICENSE.LGPL diff --git a/library/vendor/dompdf/README.md b/library/vendor/dompdf/README.md new file mode 100644 index 000000000..7546e807e --- /dev/null +++ b/library/vendor/dompdf/README.md @@ -0,0 +1,232 @@ +Dompdf +====== + +[![Build Status](https://github.com/dompdf/dompdf/actions/workflows/test.yml/badge.svg)](https://github.com/dompdf/dompdf/actions/workflows/test.yml) +[![Latest Release](https://poser.pugx.org/dompdf/dompdf/v/stable.png)](https://packagist.org/packages/dompdf/dompdf) +[![Total Downloads](https://poser.pugx.org/dompdf/dompdf/downloads.png)](https://packagist.org/packages/dompdf/dompdf) +[![License](https://poser.pugx.org/dompdf/dompdf/license.png)](https://packagist.org/packages/dompdf/dompdf) + +**Dompdf is an HTML to PDF converter** + +At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant +HTML layout and rendering engine written in PHP. It is a style-driven renderer: +it will download and read external stylesheets, inline style tags, and the style +attributes of individual HTML elements. It also supports most presentational +HTML attributes. + +*This document applies to the latest stable code which may not reflect the current +release. For released code please +[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).* + +---- + +**Check out the [demo](http://eclecticgeek.com/dompdf/debug.php) and ask any +question on [StackOverflow](https://stackoverflow.com/questions/tagged/dompdf) or +in [Discussions](https://github.com/dompdf/dompdf/discussions).** + +Follow us on [![Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf). + +--- + + + +## Features + + * Handles most CSS 2.1 and a few CSS3 properties, including @import, @media & + @page rules + * Supports most presentational HTML 4.0 attributes + * Supports external stylesheets, either local or through http/ftp (via + fopen-wrappers) + * Supports complex tables, including row & column spans, separate & collapsed + border models, individual cell styling + * Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg) + * No dependencies on external PDF libraries, thanks to the R&OS PDF class + * Inline PHP support + * Basic SVG support (see "Limitations" below) + +## Requirements + + * PHP version 7.1 or higher + * DOM extension + * MBString extension + * php-font-lib + * php-svg-lib + +Note that some required dependencies may have further dependencies +(notably php-svg-lib requires sabberworm/php-css-parser). + +### Recommendations + + * OPcache (OPcache, XCache, APC, etc.): improves performance + * GD (for image processing) + * IMagick or GMagick extension: improves image processing performance + +Visit the wiki for more information: +https://github.com/dompdf/dompdf/wiki/Requirements + +## About Fonts & Character Encoding + +PDF documents internally support the following fonts: Helvetica, Times-Roman, +Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI +encoding. In order for a PDF to display characters that are not available in +Windows ANSI, you must supply an external font. Dompdf will embed any referenced +font in the PDF so long as it has been pre-loaded or is accessible to dompdf and +reference in CSS @font-face rules. See the +[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding) +for more information on how to use fonts. + +The [DejaVu TrueType fonts](https://dejavu-fonts.github.io/) have been pre-installed +to give dompdf decent Unicode character coverage by default. To use the DejaVu +fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu +Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available: +DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono. + +## Easy Installation + +### Install with composer + +To install with [Composer](https://getcomposer.org/), simply require the +latest version of this package. + +```bash +composer require dompdf/dompdf +``` + +Make sure that the autoload file from Composer is loaded. + +```php +// somewhere early in your project's loading, require the Composer autoloader +// see: http://getcomposer.org/doc/00-intro.md +require 'vendor/autoload.php'; + +``` + +### Download and install + +Download a packaged archive of dompdf and extract it into the +directory where dompdf will reside + + * You can download stable copies of dompdf from + https://github.com/dompdf/dompdf/releases + * Or download a nightly (the latest, unreleased code) from + http://eclecticgeek.com/dompdf + +Use the packaged release autoloader to load dompdf, libraries, +and helper functions in your PHP: + +```php +// include autoloader +require_once 'dompdf/autoload.inc.php'; +``` + +Note: packaged releases are named according using semantic +versioning (_dompdf_MAJOR-MINOR-PATCH.zip_). So the 1.0.0 +release would be dompdf_1-0-0.zip. This is the only download +that includes the autoloader for Dompdf and all its dependencies. + +### Install with git + +From the command line, switch to the directory where dompdf will +reside and run the following commands: + +```sh +git clone https://github.com/dompdf/dompdf.git +cd dompdf/lib + +git clone https://github.com/PhenX/php-font-lib.git php-font-lib +cd php-font-lib +git checkout 0.5.1 +cd .. + +git clone https://github.com/PhenX/php-svg-lib.git php-svg-lib +cd php-svg-lib +git checkout v0.3.2 +cd .. + +git clone https://github.com/sabberworm/PHP-CSS-Parser.git php-css-parser +cd php-css-parser +git checkout 8.1.0 +``` + +Require dompdf and it's dependencies in your PHP. +For details see the [autoloader in the utils project](https://github.com/dompdf/utils/blob/master/autoload.inc.php). + +## Quick Start + +Just pass your HTML in to dompdf and stream the output: + +```php +// reference the Dompdf namespace +use Dompdf\Dompdf; + +// instantiate and use the dompdf class +$dompdf = new Dompdf(); +$dompdf->loadHtml('hello world'); + +// (Optional) Setup the paper size and orientation +$dompdf->setPaper('A4', 'landscape'); + +// Render the HTML as PDF +$dompdf->render(); + +// Output the generated PDF to Browser +$dompdf->stream(); +``` + +### Setting Options + +Set options during dompdf instantiation: + +```php +use Dompdf\Dompdf; +use Dompdf\Options; + +$options = new Options(); +$options->set('defaultFont', 'Courier'); +$dompdf = new Dompdf($options); +``` + +or at run time + +```php +use Dompdf\Dompdf; + +$dompdf = new Dompdf(); +$options = $dompdf->getOptions(); +$options->setDefaultFont('Courier'); +$dompdf->setOptions($options); +``` + +See [Dompdf\Options](src/Options.php) for a list of available options. + +### Resource Reference Requirements + +In order to protect potentially sensitive information Dompdf imposes +restrictions on files referenced from the local file system or the web. + +Files accessed through web-based protocols have the following requirements: + * The Dompdf option "isRemoteEnabled" must be set to "true" + * PHP must either have the curl extension enabled or the + allow_url_fopen setting set to true + +Files accessed through the local file system have the following requirement: + * The file must fall within the path(s) specified for the Dompdf "chroot" option + +## Limitations (Known Issues) + + * Table cells are not pageable, meaning a table row must fit on a single page. + * Elements are rendered on the active page when they are parsed. + * Embedding "raw" SVG's (``) isn't working yet, you need to + either link to an external SVG file, or use a DataURI like this: + ```php + $html = ''; + ``` + Watch https://github.com/dompdf/dompdf/issues/320 for progress + * Does not support CSS flexbox. + * Does not support CSS Grid. +--- + +[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf) + +*If you find this project useful, please consider making a donation. +Any funds donated will be used to help further development on this project.)* diff --git a/library/vendor/dompdf/SOURCE b/library/vendor/dompdf/SOURCE deleted file mode 100644 index b21547113..000000000 --- a/library/vendor/dompdf/SOURCE +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -eux -#GLOBIGNORE=$0; rm -rf * - -DOMPDF_VERSION=0.8.3 -PHP_FONTLIB_VERSION=0.5.1 -PHP_SVGLIB_VERSION=0.3.2 - -rm -rf lib/ src/ - -curl -LsS https://github.com/dompdf/dompdf/archive/v"$DOMPDF_VERSION".tar.gz -o /tmp/dompdf.tar.gz -tar xf /tmp/dompdf.tar.gz --strip-components 1 dompdf-"$DOMPDF_VERSION"/{lib,src,LICENSE.LGPL} -rm /tmp/dompdf.tar.gz -mv LICENSE.LGPL LICENSE - -curl -LsS https://github.com/PhenX/php-font-lib/archive/"$PHP_FONTLIB_VERSION".tar.gz -o /tmp/php-font-lib.tar.gz -[ -d lib/php-font-lib ] || mkdir -p lib/php-font-lib -tar xf /tmp/php-font-lib.tar.gz --strip-components 1 -C lib/php-font-lib php-font-lib-"$PHP_FONTLIB_VERSION"/{src,LICENSE} -rm /tmp/php-font-lib.tar.gz - -curl -LsS https://github.com/PhenX/php-svg-lib/archive/v"$PHP_SVGLIB_VERSION".tar.gz -o /tmp/php-svg-lib.tar.gz -[ -d lib/php-svg-lib ] || mkdir -p lib/php-svg-lib -tar xf /tmp/php-svg-lib.tar.gz --strip-components 1 -C lib/php-svg-lib php-svg-lib-"$PHP_SVGLIB_VERSION"/src -rm /tmp/php-svg-lib.tar.gz diff --git a/library/vendor/dompdf/VERSION b/library/vendor/dompdf/VERSION new file mode 100644 index 000000000..38f77a65b --- /dev/null +++ b/library/vendor/dompdf/VERSION @@ -0,0 +1 @@ +2.0.1 diff --git a/library/vendor/dompdf/autoload.inc.php b/library/vendor/dompdf/autoload.inc.php new file mode 100644 index 000000000..dc542ab9b --- /dev/null +++ b/library/vendor/dompdf/autoload.inc.php @@ -0,0 +1 @@ + - array( - 'normal' => $distFontDir . 'Helvetica', - 'bold' => $distFontDir . 'Helvetica-Bold', - 'italic' => $distFontDir . 'Helvetica-Oblique', - 'bold_italic' => $distFontDir . 'Helvetica-BoldOblique' - ), - 'times' => - array( - 'normal' => $distFontDir . 'Times-Roman', - 'bold' => $distFontDir . 'Times-Bold', - 'italic' => $distFontDir . 'Times-Italic', - 'bold_italic' => $distFontDir . 'Times-BoldItalic' - ), - 'times-roman' => - array( - 'normal' => $distFontDir . 'Times-Roman', - 'bold' => $distFontDir . 'Times-Bold', - 'italic' => $distFontDir . 'Times-Italic', - 'bold_italic' => $distFontDir . 'Times-BoldItalic' - ), - 'courier' => - array( - 'normal' => $distFontDir . 'Courier', - 'bold' => $distFontDir . 'Courier-Bold', - 'italic' => $distFontDir . 'Courier-Oblique', - 'bold_italic' => $distFontDir . 'Courier-BoldOblique' - ), - 'helvetica' => - array( - 'normal' => $distFontDir . 'Helvetica', - 'bold' => $distFontDir . 'Helvetica-Bold', - 'italic' => $distFontDir . 'Helvetica-Oblique', - 'bold_italic' => $distFontDir . 'Helvetica-BoldOblique' - ), - 'zapfdingbats' => - array( - 'normal' => $distFontDir . 'ZapfDingbats', - 'bold' => $distFontDir . 'ZapfDingbats', - 'italic' => $distFontDir . 'ZapfDingbats', - 'bold_italic' => $distFontDir . 'ZapfDingbats' - ), - 'symbol' => - array( - 'normal' => $distFontDir . 'Symbol', - 'bold' => $distFontDir . 'Symbol', - 'italic' => $distFontDir . 'Symbol', - 'bold_italic' => $distFontDir . 'Symbol' - ), - 'serif' => - array( - 'normal' => $distFontDir . 'Times-Roman', - 'bold' => $distFontDir . 'Times-Bold', - 'italic' => $distFontDir . 'Times-Italic', - 'bold_italic' => $distFontDir . 'Times-BoldItalic' - ), - 'monospace' => - array( - 'normal' => $distFontDir . 'Courier', - 'bold' => $distFontDir . 'Courier-Bold', - 'italic' => $distFontDir . 'Courier-Oblique', - 'bold_italic' => $distFontDir . 'Courier-BoldOblique' - ), - 'fixed' => - array( - 'normal' => $distFontDir . 'Courier', - 'bold' => $distFontDir . 'Courier-Bold', - 'italic' => $distFontDir . 'Courier-Oblique', - 'bold_italic' => $distFontDir . 'Courier-BoldOblique' - ), - 'dejavu sans' => - array( - 'bold' => $distFontDir . 'DejaVuSans-Bold', - 'bold_italic' => $distFontDir . 'DejaVuSans-BoldOblique', - 'italic' => $distFontDir . 'DejaVuSans-Oblique', - 'normal' => $distFontDir . 'DejaVuSans' - ), - 'dejavu sans mono' => - array( - 'bold' => $distFontDir . 'DejaVuSansMono-Bold', - 'bold_italic' => $distFontDir . 'DejaVuSansMono-BoldOblique', - 'italic' => $distFontDir . 'DejaVuSansMono-Oblique', - 'normal' => $distFontDir . 'DejaVuSansMono' - ), - 'dejavu serif' => - array( - 'bold' => $distFontDir . 'DejaVuSerif-Bold', - 'bold_italic' => $distFontDir . 'DejaVuSerif-BoldItalic', - 'italic' => $distFontDir . 'DejaVuSerif-Italic', - 'normal' => $distFontDir . 'DejaVuSerif' - ) -); \ No newline at end of file diff --git a/library/vendor/dompdf/lib/html5lib/Data.php b/library/vendor/dompdf/lib/html5lib/Data.php deleted file mode 100644 index f0f520880..000000000 --- a/library/vendor/dompdf/lib/html5lib/Data.php +++ /dev/null @@ -1,123 +0,0 @@ - 0xFFFD, // REPLACEMENT CHARACTER - 0x0D => 0x000A, // LINE FEED (LF) - 0x80 => 0x20AC, // EURO SIGN ('€') - 0x81 => 0x0081, // - 0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK ('‚') - 0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK ('ƒ') - 0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK ('„') - 0x85 => 0x2026, // HORIZONTAL ELLIPSIS ('…') - 0x86 => 0x2020, // DAGGER ('†') - 0x87 => 0x2021, // DOUBLE DAGGER ('‡') - 0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT ('ˆ') - 0x89 => 0x2030, // PER MILLE SIGN ('‰') - 0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON ('Š') - 0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK ('‹') - 0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE ('Œ') - 0x8D => 0x008D, // - 0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON ('Ž') - 0x8F => 0x008F, // - 0x90 => 0x0090, // - 0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK ('‘') - 0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK ('’') - 0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK ('“') - 0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK ('”') - 0x95 => 0x2022, // BULLET ('•') - 0x96 => 0x2013, // EN DASH ('–') - 0x97 => 0x2014, // EM DASH ('—') - 0x98 => 0x02DC, // SMALL TILDE ('˜') - 0x99 => 0x2122, // TRADE MARK SIGN ('™') - 0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON ('š') - 0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK ('›') - 0x9C => 0x0153, // LATIN SMALL LIGATURE OE ('œ') - 0x9D => 0x009D, // - 0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON ('ž') - 0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS ('Ÿ') - ); - - protected static $namedCharacterReferences; - - protected static $namedCharacterReferenceMaxLength; - - /** - * Returns the "real" Unicode codepoint of a malformed character - * reference. - */ - public static function getRealCodepoint($ref) { - if (!isset(self::$realCodepointTable[$ref])) { - return false; - } else { - return self::$realCodepointTable[$ref]; - } - } - - public static function getNamedCharacterReferences() { - if (!self::$namedCharacterReferences) { - self::$namedCharacterReferences = unserialize( - file_get_contents(dirname(__FILE__) . '/named-character-references.ser')); - } - return self::$namedCharacterReferences; - } - - /** - * Converts a Unicode codepoint to sequence of UTF-8 bytes. - * @note Shamelessly stolen from HTML Purifier, which is also - * shamelessly stolen from Feyd (which is in public domain). - */ - public static function utf8chr($code) { - /* We don't care: we live dangerously - * if($code > 0x10FFFF or $code < 0x0 or - ($code >= 0xD800 and $code <= 0xDFFF) ) { - // bits are set outside the "valid" range as defined - // by UNICODE 4.1.0 - return "\xEF\xBF\xBD"; - }*/ - - $y = $z = $w = 0; - if ($code < 0x80) { - // regular ASCII character - $x = $code; - } else { - // set up bits for UTF-8 - $x = ($code & 0x3F) | 0x80; - if ($code < 0x800) { - $y = (($code & 0x7FF) >> 6) | 0xC0; - } else { - $y = (($code & 0xFC0) >> 6) | 0x80; - if ($code < 0x10000) { - $z = (($code >> 12) & 0x0F) | 0xE0; - } else { - $z = (($code >> 12) & 0x3F) | 0x80; - $w = (($code >> 18) & 0x07) | 0xF0; - } - } - } - // set up the actual character - $ret = ''; - if ($w) { - $ret .= chr($w); - } - if ($z) { - $ret .= chr($z); - } - if ($y) { - $ret .= chr($y); - } - $ret .= chr($x); - - return $ret; - } - -} diff --git a/library/vendor/dompdf/lib/html5lib/InputStream.php b/library/vendor/dompdf/lib/html5lib/InputStream.php deleted file mode 100644 index 65fc14f31..000000000 --- a/library/vendor/dompdf/lib/html5lib/InputStream.php +++ /dev/null @@ -1,299 +0,0 @@ - - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -// Some conventions: -// /* */ indicates verbatim text from the HTML 5 specification -// // indicates regular comments - -class HTML5_InputStream { - /** - * The string data we're parsing. - */ - private $data; - - /** - * The current integer byte position we are in $data - */ - private $char; - - /** - * Length of $data; when $char === $data, we are at the end-of-file. - */ - private $EOF; - - /** - * Parse errors. - */ - public $errors = array(); - - /** - * @param $data | Data to parse - * @throws Exception - */ - public function __construct($data) { - - /* Given an encoding, the bytes in the input stream must be - converted to Unicode characters for the tokeniser, as - described by the rules for that encoding, except that the - leading U+FEFF BYTE ORDER MARK character, if any, must not - be stripped by the encoding layer (it is stripped by the rule below). - - Bytes or sequences of bytes in the original byte stream that - could not be converted to Unicode characters must be converted - to U+FFFD REPLACEMENT CHARACTER code points. */ - - // XXX currently assuming input data is UTF-8; once we - // build encoding detection this will no longer be the case - // - // We previously had an mbstring implementation here, but that - // implementation is heavily non-conforming, so it's been - // omitted. - if (extension_loaded('iconv')) { - // non-conforming - $data = @iconv('UTF-8', 'UTF-8//IGNORE', $data); - } else { - // we can make a conforming native implementation - throw new Exception('Not implemented, please install iconv'); - } - - /* One leading U+FEFF BYTE ORDER MARK character must be - ignored if any are present. */ - if (substr($data, 0, 3) === "\xEF\xBB\xBF") { - $data = substr($data, 3); - } - - /* All U+0000 NULL characters in the input must be replaced - by U+FFFD REPLACEMENT CHARACTERs. Any occurrences of such - characters is a parse error. */ - for ($i = 0, $count = substr_count($data, "\0"); $i < $count; $i++) { - $this->errors[] = array( - 'type' => HTML5_Tokenizer::PARSEERROR, - 'data' => 'null-character' - ); - } - /* U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED - (LF) characters are treated specially. Any CR characters - that are followed by LF characters must be removed, and any - CR characters not followed by LF characters must be converted - to LF characters. Thus, newlines in HTML DOMs are represented - by LF characters, and there are never any CR characters in the - input to the tokenization stage. */ - $data = str_replace( - array( - "\0", - "\r\n", - "\r" - ), - array( - "\xEF\xBF\xBD", - "\n", - "\n" - ), - $data - ); - - /* Any occurrences of any characters in the ranges U+0001 to - U+0008, U+000B, U+000E to U+001F, U+007F to U+009F, - U+D800 to U+DFFF , U+FDD0 to U+FDEF, and - characters U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, U+2FFFE, U+2FFFF, - U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE, - U+6FFFF, U+7FFFE, U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, - U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, U+DFFFE, - U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, and - U+10FFFF are parse errors. (These are all control characters - or permanently undefined Unicode characters.) */ - // Check PCRE is loaded. - if (extension_loaded('pcre')) { - $count = preg_match_all( - '/(?: - [\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F - | - \xC2[\x80-\x9F] # U+0080 to U+009F - | - \xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF - | - \xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF - | - \xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF - | - [\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16}) - )/x', - $data, - $matches - ); - for ($i = 0; $i < $count; $i++) { - $this->errors[] = array( - 'type' => HTML5_Tokenizer::PARSEERROR, - 'data' => 'invalid-codepoint' - ); - } - } else { - // XXX: Need non-PCRE impl, probably using substr_count - } - - $this->data = $data; - $this->char = 0; - $this->EOF = strlen($data); - } - - /** - * Returns the current line that the tokenizer is at. - * - * @return int - */ - public function getCurrentLine() { - // Check the string isn't empty - if ($this->EOF) { - // Add one to $this->char because we want the number for the next - // byte to be processed. - return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1; - } else { - // If the string is empty, we are on the first line (sorta). - return 1; - } - } - - /** - * Returns the current column of the current line that the tokenizer is at. - * - * @return int - */ - public function getColumnOffset() { - // strrpos is weird, and the offset needs to be negative for what we - // want (i.e., the last \n before $this->char). This needs to not have - // one (to make it point to the next character, the one we want the - // position of) added to it because strrpos's behaviour includes the - // final offset byte. - $lastLine = strrpos($this->data, "\n", $this->char - 1 - strlen($this->data)); - - // However, for here we want the length up until the next byte to be - // processed, so add one to the current byte ($this->char). - if ($lastLine !== false) { - $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine); - } else { - $findLengthOf = substr($this->data, 0, $this->char); - } - - // Get the length for the string we need. - if (extension_loaded('iconv')) { - return iconv_strlen($findLengthOf, 'utf-8'); - } elseif (extension_loaded('mbstring')) { - return mb_strlen($findLengthOf, 'utf-8'); - } elseif (extension_loaded('xml')) { - return strlen(utf8_decode($findLengthOf)); - } else { - $count = count_chars($findLengthOf); - // 0x80 = 0x7F - 0 + 1 (one added to get inclusive range) - // 0x33 = 0xF4 - 0x2C + 1 (one added to get inclusive range) - return array_sum(array_slice($count, 0, 0x80)) + - array_sum(array_slice($count, 0xC2, 0x33)); - } - } - - /** - * Retrieve the currently consume character. - * @note This performs bounds checking - * - * @return bool|string - */ - public function char() { - return ($this->char++ < $this->EOF) - ? $this->data[$this->char - 1] - : false; - } - - /** - * Get all characters until EOF. - * @note This performs bounds checking - * - * @return string|bool - */ - public function remainingChars() { - if ($this->char < $this->EOF) { - $data = substr($this->data, $this->char); - $this->char = $this->EOF; - return $data; - } else { - return false; - } - } - - /** - * Matches as far as possible until we reach a certain set of bytes - * and returns the matched substring. - * - * @param $bytes | Bytes to match. - * @param null $max - * @return bool|string - */ - public function charsUntil($bytes, $max = null) { - if ($this->char < $this->EOF) { - if ($max === 0 || $max) { - $len = strcspn($this->data, $bytes, $this->char, $max); - } else { - $len = strcspn($this->data, $bytes, $this->char); - } - $string = (string) substr($this->data, $this->char, $len); - $this->char += $len; - return $string; - } else { - return false; - } - } - - /** - * Matches as far as possible with a certain set of bytes - * and returns the matched substring. - * - * @param $bytes | Bytes to match. - * @param null $max - * @return bool|string - */ - public function charsWhile($bytes, $max = null) { - if ($this->char < $this->EOF) { - if ($max === 0 || $max) { - $len = strspn($this->data, $bytes, $this->char, $max); - } else { - $len = strspn($this->data, $bytes, $this->char); - } - $string = (string) substr($this->data, $this->char, $len); - $this->char += $len; - return $string; - } else { - return false; - } - } - - /** - * Unconsume one character. - */ - public function unget() { - if ($this->char <= $this->EOF) { - $this->char--; - } - } -} diff --git a/library/vendor/dompdf/lib/html5lib/Parser.php b/library/vendor/dompdf/lib/html5lib/Parser.php deleted file mode 100644 index 724fa9af6..000000000 --- a/library/vendor/dompdf/lib/html5lib/Parser.php +++ /dev/null @@ -1,37 +0,0 @@ -parse(); - return $tokenizer->save(); - } - - /** - * Parses an HTML fragment. - * @param $text | HTML text to parse - * @param $context String name of context element to pretend parsing is in. - * @param $builder | Custom builder implementation - * @return DOMDocument|DOMNodeList Parsed HTML as DOMDocument - */ - static public function parseFragment($text, $context = null, $builder = null) { - $tokenizer = new HTML5_Tokenizer($text, $builder); - $tokenizer->parseFragment($context); - return $tokenizer->save(); - } -} diff --git a/library/vendor/dompdf/lib/html5lib/Tokenizer.php b/library/vendor/dompdf/lib/html5lib/Tokenizer.php deleted file mode 100644 index 46e8504f2..000000000 --- a/library/vendor/dompdf/lib/html5lib/Tokenizer.php +++ /dev/null @@ -1,2470 +0,0 @@ - -Copyright 2008 Edward Z. Yang -Copyright 2009 Geoffrey Sneddon - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -// Some conventions: -// /* */ indicates verbatim text from the HTML 5 specification -// // indicates regular comments - -// all flags are in hyphenated form - -class HTML5_Tokenizer { - /** - * @var HTML5_InputStream - * - * Points to an InputStream object. - */ - protected $stream; - - /** - * @var HTML5_TreeBuilder - * - * Tree builder that the tokenizer emits token to. - */ - private $tree; - - /** - * @var int - * - * Current content model we are parsing as. - */ - protected $content_model; - - /** - * Current token that is being built, but not yet emitted. Also - * is the last token emitted, if applicable. - */ - protected $token; - - // These are constants describing the content model - const PCDATA = 0; - const RCDATA = 1; - const CDATA = 2; - const PLAINTEXT = 3; - - // These are constants describing tokens - // XXX should probably be moved somewhere else, probably the - // HTML5 class. - const DOCTYPE = 0; - const STARTTAG = 1; - const ENDTAG = 2; - const COMMENT = 3; - const CHARACTER = 4; - const SPACECHARACTER = 5; - const EOF = 6; - const PARSEERROR = 7; - - // These are constants representing bunches of characters. - const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - const UPPER_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - const LOWER_ALPHA = 'abcdefghijklmnopqrstuvwxyz'; - const DIGIT = '0123456789'; - const HEX = '0123456789ABCDEFabcdef'; - const WHITESPACE = "\t\n\x0c "; - - /** - * @param $data | Data to parse - * @param HTML5_TreeBuilder|null $builder - */ - public function __construct($data, $builder = null) { - $this->stream = new HTML5_InputStream($data); - if (!$builder) { - $this->tree = new HTML5_TreeBuilder; - } else { - $this->tree = $builder; - } - $this->content_model = self::PCDATA; - } - - /** - * @param null $context - */ - public function parseFragment($context = null) { - $this->tree->setupContext($context); - if ($this->tree->content_model) { - $this->content_model = $this->tree->content_model; - $this->tree->content_model = null; - } - $this->parse(); - } - - // XXX maybe convert this into an iterator? regardless, this function - // and the save function should go into a Parser facade of some sort - /** - * Performs the actual parsing of the document. - */ - public function parse() { - // Current state - $state = 'data'; - // This is used to avoid having to have look-behind in the data state. - $lastFourChars = ''; - /** - * Escape flag as specified by the HTML5 specification: "used to - * control the behavior of the tokeniser. It is either true or - * false, and initially must be set to the false state." - */ - $escape = false; - //echo "\n\n"; - while($state !== null) { - - /*echo $state . ' '; - switch ($this->content_model) { - case self::PCDATA: echo 'PCDATA'; break; - case self::RCDATA: echo 'RCDATA'; break; - case self::CDATA: echo 'CDATA'; break; - case self::PLAINTEXT: echo 'PLAINTEXT'; break; - } - if ($escape) echo " escape"; - echo "\n";*/ - - switch($state) { - case 'data': - - /* Consume the next input character */ - $char = $this->stream->char(); - $lastFourChars .= $char; - if (strlen($lastFourChars) > 4) { - $lastFourChars = substr($lastFourChars, -4); - } - - // see below for meaning - $hyp_cond = - !$escape && - ( - $this->content_model === self::RCDATA || - $this->content_model === self::CDATA - ); - $amp_cond = - !$escape && - ( - $this->content_model === self::PCDATA || - $this->content_model === self::RCDATA - ); - $lt_cond = - $this->content_model === self::PCDATA || - ( - ( - $this->content_model === self::RCDATA || - $this->content_model === self::CDATA - ) && - !$escape - ); - $gt_cond = - $escape && - ( - $this->content_model === self::RCDATA || - $this->content_model === self::CDATA - ); - - if ($char === '&' && $amp_cond === true) { - /* U+0026 AMPERSAND (&) - When the content model flag is set to one of the PCDATA or RCDATA - states and the escape flag is false: switch to the - character reference data state. Otherwise: treat it as per - the "anything else" entry below. */ - $state = 'character reference data'; - - } elseif ( - $char === '-' && - $hyp_cond === true && - $lastFourChars === '' - ) { - /* If the content model flag is set to either the RCDATA state or - the CDATA state, and the escape flag is true, and the last three - characters in the input stream including this one are U+002D - HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"), - set the escape flag to false. */ - $escape = false; - - /* In any case, emit the input character as a character token. - Stay in the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => '>' - )); - // We do the "any case" part as part of "anything else". - - } elseif ($char === false) { - /* EOF - Emit an end-of-file token. */ - $state = null; - $this->tree->emitToken(array( - 'type' => self::EOF - )); - - } elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - // Directly after emitting a token you switch back to the "data - // state". At that point spaceCharacters are important so they are - // emitted separately. - $chars = $this->stream->charsWhile(self::WHITESPACE); - $this->emitToken(array( - 'type' => self::SPACECHARACTER, - 'data' => $char . $chars - )); - $lastFourChars .= $chars; - if (strlen($lastFourChars) > 4) { - $lastFourChars = substr($lastFourChars, -4); - } - } else { - /* Anything else - THIS IS AN OPTIMIZATION: Get as many character that - otherwise would also be treated as a character token and emit it - as a single character token. Stay in the data state. */ - - $mask = ''; - if ($hyp_cond === true) { - $mask .= '-'; - } - if ($amp_cond === true) { - $mask .= '&'; - } - if ($lt_cond === true) { - $mask .= '<'; - } - if ($gt_cond === true) { - $mask .= '>'; - } - - if ($mask === '') { - $chars = $this->stream->remainingChars(); - } else { - $chars = $this->stream->charsUntil($mask); - } - - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => $char . $chars - )); - - $lastFourChars .= $chars; - if (strlen($lastFourChars) > 4) { - $lastFourChars = substr($lastFourChars, -4); - } - - $state = 'data'; - } - break; - - case 'character reference data': - /* (This cannot happen if the content model flag - is set to the CDATA state.) */ - - /* Attempt to consume a character reference, with no - additional allowed character. */ - $entity = $this->consumeCharacterReference(); - - /* If nothing is returned, emit a U+0026 AMPERSAND - character token. Otherwise, emit the character token that - was returned. */ - // This is all done when consuming the character reference. - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => $entity - )); - - /* Finally, switch to the data state. */ - $state = 'data'; - break; - - case 'tag open': - $char = $this->stream->char(); - - switch ($this->content_model) { - case self::RCDATA: - case self::CDATA: - /* Consume the next input character. If it is a - U+002F SOLIDUS (/) character, switch to the close - tag open state. Otherwise, emit a U+003C LESS-THAN - SIGN character token and reconsume the current input - character in the data state. */ - // We consumed above. - - if ($char === '/') { - $state = 'close tag open'; - } else { - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => '<' - )); - - $this->stream->unget(); - - $state = 'data'; - } - break; - - case self::PCDATA: - /* If the content model flag is set to the PCDATA state - Consume the next input character: */ - // We consumed above. - - if ($char === '!') { - /* U+0021 EXCLAMATION MARK (!) - Switch to the markup declaration open state. */ - $state = 'markup declaration open'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the close tag open state. */ - $state = 'close tag open'; - - } elseif ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new start tag token, set its tag name to the lowercase - version of the input character (add 0x0020 to the character's code - point), then switch to the tag name state. (Don't emit the token - yet; further details will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::STARTTAG, - 'attr' => array() - ); - - $state = 'tag name'; - - } elseif ('a' <= $char && $char <= 'z') { - /* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z - Create a new start tag token, set its tag name to the input - character, then switch to the tag name state. (Don't emit - the token yet; further details will be filled in before it - is emitted.) */ - $this->token = array( - 'name' => $char, - 'type' => self::STARTTAG, - 'attr' => array() - ); - - $state = 'tag name'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Emit a U+003C LESS-THAN SIGN character token and a - U+003E GREATER-THAN SIGN character token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-tag-name-but-got-right-bracket' - )); - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => '<>' - )); - - $state = 'data'; - - } elseif ($char === '?') { - /* U+003F QUESTION MARK (?) - Parse error. Switch to the bogus comment state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-tag-name-but-got-question-mark' - )); - $this->token = array( - 'data' => '?', - 'type' => self::COMMENT - ); - $state = 'bogus comment'; - - } else { - /* Anything else - Parse error. Emit a U+003C LESS-THAN SIGN character token and - reconsume the current input character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-tag-name' - )); - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => '<' - )); - - $state = 'data'; - $this->stream->unget(); - } - break; - } - break; - - case 'close tag open': - if ( - $this->content_model === self::RCDATA || - $this->content_model === self::CDATA - ) { - /* If the content model flag is set to the RCDATA or CDATA - states... */ - $name = strtolower($this->stream->charsWhile(self::ALPHA)); - $following = $this->stream->char(); - $this->stream->unget(); - if ( - !$this->token || - $this->token['name'] !== $name || - $this->token['name'] === $name && !in_array($following, array("\x09", "\x0A", "\x0C", "\x20", "\x3E", "\x2F", false)) - ) { - /* if no start tag token has ever been emitted by this instance - of the tokenizer (fragment case), or, if the next few - characters do not match the tag name of the last start tag - token emitted (compared in an ASCII case-insensitive manner), - or if they do but they are not immediately followed by one of - the following characters: - - * U+0009 CHARACTER TABULATION - * U+000A LINE FEED (LF) - * U+000C FORM FEED (FF) - * U+0020 SPACE - * U+003E GREATER-THAN SIGN (>) - * U+002F SOLIDUS (/) - * EOF - - ...then emit a U+003C LESS-THAN SIGN character token, a - U+002F SOLIDUS character token, and switch to the data - state to process the next input character. */ - // XXX: Probably ought to replace in_array with $following === x ||... - - // We also need to emit $name now we've consumed that, as we - // know it'll just be emitted as a character token. - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => 'token = array( - 'name' => $name, - 'type' => self::ENDTAG - ); - - // Change to tag name state. - $state = 'tag name'; - } - } elseif ($this->content_model === self::PCDATA) { - /* Otherwise, if the content model flag is set to the PCDATA - state [...]: */ - $char = $this->stream->char(); - - if ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new end tag token, set its tag name to the lowercase version - of the input character (add 0x0020 to the character's code point), then - switch to the tag name state. (Don't emit the token yet; further details - will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::ENDTAG - ); - - $state = 'tag name'; - - } elseif ('a' <= $char && $char <= 'z') { - /* U+0061 LATIN SMALL LETTER A through to U+007A LATIN SMALL LETTER Z - Create a new end tag token, set its tag name to the - input character, then switch to the tag name state. - (Don't emit the token yet; further details will be - filled in before it is emitted.) */ - $this->token = array( - 'name' => $char, - 'type' => self::ENDTAG - ); - - $state = 'tag name'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-closing-tag-but-got-right-bracket' - )); - $state = 'data'; - - } elseif ($char === false) { - /* EOF - Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F - SOLIDUS character token. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-closing-tag-but-got-eof' - )); - $this->emitToken(array( - 'type' => self::CHARACTER, - 'data' => 'stream->unget(); - $state = 'data'; - - } else { - /* Parse error. Switch to the bogus comment state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-closing-tag-but-got-char' - )); - $this->token = array( - 'data' => $char, - 'type' => self::COMMENT - ); - $state = 'bogus comment'; - } - } - break; - - case 'tag name': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $state = 'before attribute name'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the self-closing start tag state. */ - $state = 'self-closing start tag'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z - Append the lowercase version of the current input - character (add 0x0020 to the character's code point) to - the current tag token's tag name. Stay in the tag name state. */ - $chars = $this->stream->charsWhile(self::UPPER_ALPHA); - - $this->token['name'] .= strtolower($char . $chars); - $state = 'tag name'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-tag-name' - )); - - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Append the current input character to the current tag token's tag name. - Stay in the tag name state. */ - $chars = $this->stream->charsUntil("\t\n\x0C />" . self::UPPER_ALPHA); - - $this->token['name'] .= $char . $chars; - $state = 'tag name'; - } - break; - - case 'before attribute name': - /* Consume the next input character: */ - $char = $this->stream->char(); - - // this conditional is optimized, check bottom - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute name state. */ - $state = 'before attribute name'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the self-closing start tag state. */ - $state = 'self-closing start tag'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z - Start a new attribute in the current tag token. Set that - attribute's name to the lowercase version of the current - input character (add 0x0020 to the character's code - point), and its value to the empty string. Switch to the - attribute name state.*/ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => '' - ); - - $state = 'attribute name'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-attribute-name-but-got-eof' - )); - - $this->stream->unget(); - $state = 'data'; - - } else { - /* U+0022 QUOTATION MARK (") - U+0027 APOSTROPHE (') - U+003C LESS-THAN SIGN (<) - U+003D EQUALS SIGN (=) - Parse error. Treat it as per the "anything else" entry - below. */ - if ($char === '"' || $char === "'" || $char === '<' || $char === '=') { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'invalid-character-in-attribute-name' - )); - } - - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => $char, - 'value' => '' - ); - - $state = 'attribute name'; - } - break; - - case 'attribute name': - // Consume the next input character: - $char = $this->stream->char(); - - // this conditional is optimized, check bottom - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the after attribute name state. */ - $state = 'after attribute name'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the self-closing start tag state. */ - $state = 'self-closing start tag'; - - } elseif ($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $state = 'before attribute value'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z - Append the lowercase version of the current input - character (add 0x0020 to the character's code point) to - the current attribute's name. Stay in the attribute name - state. */ - $chars = $this->stream->charsWhile(self::UPPER_ALPHA); - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['name'] .= strtolower($char . $chars); - - $state = 'attribute name'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-attribute-name' - )); - - $this->stream->unget(); - $state = 'data'; - - } else { - /* U+0022 QUOTATION MARK (") - U+0027 APOSTROPHE (') - U+003C LESS-THAN SIGN (<) - Parse error. Treat it as per the "anything else" - entry below. */ - if ($char === '"' || $char === "'" || $char === '<') { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'invalid-character-in-attribute-name' - )); - } - - /* Anything else - Append the current input character to the current attribute's name. - Stay in the attribute name state. */ - $chars = $this->stream->charsUntil("\t\n\x0C /=>\"'" . self::UPPER_ALPHA); - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['name'] .= $char . $chars; - - $state = 'attribute name'; - } - - /* When the user agent leaves the attribute name state - (and before emitting the tag token, if appropriate), the - complete attribute's name must be compared to the other - attributes on the same token; if there is already an - attribute on the token with the exact same name, then this - is a parse error and the new attribute must be dropped, along - with the value that gets associated with it (if any). */ - // this might be implemented in the emitToken method - break; - - case 'after attribute name': - // Consume the next input character: - $char = $this->stream->char(); - - // this is an optimized conditional, check the bottom - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the after attribute name state. */ - $state = 'after attribute name'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the self-closing start tag state. */ - $state = 'self-closing start tag'; - - } elseif ($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $state = 'before attribute value'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z - Start a new attribute in the current tag token. Set that - attribute's name to the lowercase version of the current - input character (add 0x0020 to the character's code - point), and its value to the empty string. Switch to the - attribute name state. */ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => '' - ); - - $state = 'attribute name'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-end-of-tag-but-got-eof' - )); - - $this->stream->unget(); - $state = 'data'; - - } else { - /* U+0022 QUOTATION MARK (") - U+0027 APOSTROPHE (') - U+003C LESS-THAN SIGN(<) - Parse error. Treat it as per the "anything else" - entry below. */ - if ($char === '"' || $char === "'" || $char === "<") { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'invalid-character-after-attribute-name' - )); - } - - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => $char, - 'value' => '' - ); - - $state = 'attribute name'; - } - break; - - case 'before attribute value': - // Consume the next input character: - $char = $this->stream->char(); - - // this is an optimized conditional - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute value state. */ - $state = 'before attribute value'; - - } elseif ($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the attribute value (double-quoted) state. */ - $state = 'attribute value (double-quoted)'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the attribute value (unquoted) state and reconsume - this input character. */ - $this->stream->unget(); - $state = 'attribute value (unquoted)'; - - } elseif ($char === '\'') { - /* U+0027 APOSTROPHE (') - Switch to the attribute value (single-quoted) state. */ - $state = 'attribute value (single-quoted)'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Emit the current tag token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-attribute-value-but-got-right-bracket' - )); - $this->emitToken($this->token); - $state = 'data'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-attribute-value-but-got-eof' - )); - $this->stream->unget(); - $state = 'data'; - - } else { - /* U+003D EQUALS SIGN (=) - * U+003C LESS-THAN SIGN (<) - Parse error. Treat it as per the "anything else" entry below. */ - if ($char === '=' || $char === '<') { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'equals-in-unquoted-attribute-value' - )); - } - - /* Anything else - Append the current input character to the current attribute's value. - Switch to the attribute value (unquoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $state = 'attribute value (unquoted)'; - } - break; - - case 'attribute value (double-quoted)': - // Consume the next input character: - $char = $this->stream->char(); - - if ($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the after attribute value (quoted) state. */ - $state = 'after attribute value (quoted)'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the character reference in attribute value - state, with the additional allowed character - being U+0022 QUOTATION MARK ("). */ - $this->characterReferenceInAttributeValue('"'); - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-attribute-value-double-quote' - )); - - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (double-quoted) state. */ - $chars = $this->stream->charsUntil('"&'); - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char . $chars; - - $state = 'attribute value (double-quoted)'; - } - break; - - case 'attribute value (single-quoted)': - // Consume the next input character: - $char = $this->stream->char(); - - if ($char === "'") { - /* U+0022 QUOTATION MARK (') - Switch to the after attribute value state. */ - $state = 'after attribute value (quoted)'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->characterReferenceInAttributeValue("'"); - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-attribute-value-single-quote' - )); - - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (single-quoted) state. */ - $chars = $this->stream->charsUntil("'&"); - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char . $chars; - - $state = 'attribute value (single-quoted)'; - } - break; - - case 'attribute value (unquoted)': - // Consume the next input character: - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $state = 'before attribute name'; - - } elseif ($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state, with the - additional allowed character being U+003E - GREATER-THAN SIGN (>). */ - $this->characterReferenceInAttributeValue('>'); - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-attribute-value-no-quotes' - )); - $this->stream->unget(); - $state = 'data'; - - } else { - /* U+0022 QUOTATION MARK (") - U+0027 APOSTROPHE (') - U+003C LESS-THAN SIGN (<) - U+003D EQUALS SIGN (=) - Parse error. Treat it as per the "anything else" - entry below. */ - if ($char === '"' || $char === "'" || $char === '=' || $char == '<') { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-character-in-unquoted-attribute-value' - )); - } - - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (unquoted) state. */ - $chars = $this->stream->charsUntil("\t\n\x0c &>\"'="); - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char . $chars; - - $state = 'attribute value (unquoted)'; - } - break; - - case 'after attribute value (quoted)': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $state = 'before attribute name'; - - } elseif ($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the self-closing start tag state. */ - $state = 'self-closing start tag'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-EOF-after-attribute-value' - )); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Parse error. Reconsume the character in the before attribute - name state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-character-after-attribute-value' - )); - $this->stream->unget(); - $state = 'before attribute name'; - } - break; - - case 'self-closing start tag': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Set the self-closing flag of the current tag token. - Emit the current tag token. Switch to the data state. */ - // not sure if this is the name we want - $this->token['self-closing'] = true; - $this->emitToken($this->token); - $state = 'data'; - - } elseif ($char === false) { - /* EOF - Parse error. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-eof-after-self-closing' - )); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Parse error. Reconsume the character in the before attribute name state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-character-after-self-closing' - )); - $this->stream->unget(); - $state = 'before attribute name'; - } - break; - - case 'bogus comment': - /* (This can only happen if the content model flag is set to the PCDATA state.) */ - /* Consume every character up to the first U+003E GREATER-THAN SIGN - character (>) or the end of the file (EOF), whichever comes first. Emit - a comment token whose data is the concatenation of all the characters - starting from and including the character that caused the state machine - to switch into the bogus comment state, up to and including the last - consumed character before the U+003E character, if any, or up to the - end of the file otherwise. (If the comment was started by the end of - the file (EOF), the token is empty.) */ - $this->token['data'] .= (string) $this->stream->charsUntil('>'); - $this->stream->char(); - - $this->emitToken($this->token); - - /* Switch to the data state. */ - $state = 'data'; - break; - - case 'markup declaration open': - // Consume for below - $hyphens = $this->stream->charsWhile('-', 2); - if ($hyphens === '-') { - $this->stream->unget(); - } - if ($hyphens !== '--') { - $alpha = $this->stream->charsWhile(self::ALPHA, 7); - } - - /* If the next two characters are both U+002D HYPHEN-MINUS (-) - characters, consume those two characters, create a comment token whose - data is the empty string, and switch to the comment state. */ - if ($hyphens === '--') { - $state = 'comment start'; - $this->token = array( - 'data' => '', - 'type' => self::COMMENT - ); - - /* Otherwise if the next seven characters are a case-insensitive match - for the word "DOCTYPE", then consume those characters and switch to the - DOCTYPE state. */ - } elseif (strtoupper($alpha) === 'DOCTYPE') { - $state = 'DOCTYPE'; - - // XXX not implemented - /* Otherwise, if the insertion mode is "in foreign content" - and the current node is not an element in the HTML namespace - and the next seven characters are an ASCII case-sensitive - match for the string "[CDATA[" (the five uppercase letters - "CDATA" with a U+005B LEFT SQUARE BRACKET character before - and after), then consume those characters and switch to the - CDATA section state (which is unrelated to the content model - flag's CDATA state). */ - - /* Otherwise, is is a parse error. Switch to the bogus comment state. - The next character that is consumed, if any, is the first character - that will be in the comment. */ - } else { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-dashes-or-doctype' - )); - $this->token = array( - 'data' => (string) $alpha, - 'type' => self::COMMENT - ); - $state = 'bogus comment'; - } - break; - - case 'comment start': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '-') { - /* U+002D HYPHEN-MINUS (-) - Switch to the comment start dash state. */ - $state = 'comment start dash'; - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Emit the comment token. Switch to the - data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'incorrect-comment' - )); - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* EOF - Parse error. Emit the comment token. Reconsume the - EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-comment' - )); - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Anything else - Append the input character to the comment token's - data. Switch to the comment state. */ - $this->token['data'] .= $char; - $state = 'comment'; - } - break; - - case 'comment start dash': - /* Consume the next input character: */ - $char = $this->stream->char(); - if ($char === '-') { - /* U+002D HYPHEN-MINUS (-) - Switch to the comment end state */ - $state = 'comment end'; - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Emit the comment token. Switch to the - data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'incorrect-comment' - )); - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* Parse error. Emit the comment token. Reconsume the - EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-comment' - )); - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - $this->token['data'] .= '-' . $char; - $state = 'comment'; - } - break; - - case 'comment': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '-') { - /* U+002D HYPHEN-MINUS (-) - Switch to the comment end dash state */ - $state = 'comment end dash'; - - } elseif ($char === false) { - /* EOF - Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-comment' - )); - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Append the input character to the comment token's data. Stay in - the comment state. */ - $chars = $this->stream->charsUntil('-'); - - $this->token['data'] .= $char . $chars; - } - break; - - case 'comment end dash': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '-') { - /* U+002D HYPHEN-MINUS (-) - Switch to the comment end state */ - $state = 'comment end'; - - } elseif ($char === false) { - /* EOF - Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-comment-end-dash' - )); - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Append a U+002D HYPHEN-MINUS (-) character and the input - character to the comment token's data. Switch to the comment state. */ - $this->token['data'] .= '-'.$char; - $state = 'comment'; - } - break; - - case 'comment end': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the comment token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ($char === '-') { - /* U+002D HYPHEN-MINUS (-) - Parse error. Append a U+002D HYPHEN-MINUS (-) character - to the comment token's data. Stay in the comment end - state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-dash-after-double-dash-in-comment' - )); - $this->token['data'] .= '-'; - - } elseif ($char === "\t" || $char === "\n" || $char === "\x0a" || $char === ' ') { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-space-after-double-dash-in-comment' - )); - $this->token['data'] .= '--' . $char; - $state = 'comment end space'; - - } elseif ($char === '!') { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-bang-after-double-dash-in-comment' - )); - $state = 'comment end bang'; - - } elseif ($char === false) { - /* EOF - Parse error. Emit the comment token. Reconsume the - EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-comment-double-dash' - )); - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Parse error. Append two U+002D HYPHEN-MINUS (-) - characters and the input character to the comment token's - data. Switch to the comment state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-char-in-comment' - )); - $this->token['data'] .= '--'.$char; - $state = 'comment'; - } - break; - - case 'comment end bang': - $char = $this->stream->char(); - if ($char === '>') { - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === "-") { - $this->token['data'] .= '--!'; - $state = 'comment end dash'; - } elseif ($char === false) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-comment-end-bang' - )); - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - $this->token['data'] .= '--!' . $char; - $state = 'comment'; - } - break; - - case 'comment end space': - $char = $this->stream->char(); - if ($char === '>') { - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === '-') { - $state = 'comment end dash'; - } elseif ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - $this->token['data'] .= $char; - } elseif ($char === false) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-eof-in-comment-end-space', - )); - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - $this->token['data'] .= $char; - $state = 'comment'; - } - break; - - case 'DOCTYPE': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before DOCTYPE name state. */ - $state = 'before DOCTYPE name'; - - } elseif ($char === false) { - /* EOF - Parse error. Create a new DOCTYPE token. Set its - force-quirks flag to on. Emit the token. Reconsume the - EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'need-space-after-doctype-but-got-eof' - )); - $this->emitToken(array( - 'name' => '', - 'type' => self::DOCTYPE, - 'force-quirks' => true, - 'error' => true - )); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Parse error. Reconsume the current character in the - before DOCTYPE name state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'need-space-after-doctype' - )); - $this->stream->unget(); - $state = 'before DOCTYPE name'; - } - break; - - case 'before DOCTYPE name': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before DOCTYPE name state. */ - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Create a new DOCTYPE token. Set its - force-quirks flag to on. Emit the token. Switch to the - data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-doctype-name-but-got-right-bracket' - )); - $this->emitToken(array( - 'name' => '', - 'type' => self::DOCTYPE, - 'force-quirks' => true, - 'error' => true - )); - - $state = 'data'; - - } elseif ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z - Create a new DOCTYPE token. Set the token's name to the - lowercase version of the input character (add 0x0020 to - the character's code point). Switch to the DOCTYPE name - state. */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::DOCTYPE, - 'error' => true - ); - - $state = 'DOCTYPE name'; - - } elseif ($char === false) { - /* EOF - Parse error. Create a new DOCTYPE token. Set its - force-quirks flag to on. Emit the token. Reconsume the - EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-doctype-name-but-got-eof' - )); - $this->emitToken(array( - 'name' => '', - 'type' => self::DOCTYPE, - 'force-quirks' => true, - 'error' => true - )); - - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Create a new DOCTYPE token. Set the token's name to the - current input character. Switch to the DOCTYPE name state. */ - $this->token = array( - 'name' => $char, - 'type' => self::DOCTYPE, - 'error' => true - ); - - $state = 'DOCTYPE name'; - } - break; - - case 'DOCTYPE name': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the after DOCTYPE name state. */ - $state = 'after DOCTYPE name'; - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current DOCTYPE token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ('A' <= $char && $char <= 'Z') { - /* U+0041 LATIN CAPITAL LETTER A through to U+005A LATIN CAPITAL LETTER Z - Append the lowercase version of the input character - (add 0x0020 to the character's code point) to the current - DOCTYPE token's name. Stay in the DOCTYPE name state. */ - $this->token['name'] .= strtolower($char); - - } elseif ($char === false) { - /* EOF - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype-name' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Append the current input character to the current - DOCTYPE token's name. Stay in the DOCTYPE name state. */ - $this->token['name'] .= $char; - } - - // XXX this is probably some sort of quirks mode designation, - // check tree-builder to be sure. In general 'error' needs - // to be specc'ified, this probably means removing it at the end - $this->token['error'] = ($this->token['name'] === 'HTML') - ? false - : true; - break; - - case 'after DOCTYPE name': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the after DOCTYPE name state. */ - - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current DOCTYPE token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ($char === false) { - /* EOF - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else */ - - $nextSix = strtoupper($char . $this->stream->charsWhile(self::ALPHA, 5)); - if ($nextSix === 'PUBLIC') { - /* If the next six characters are an ASCII - case-insensitive match for the word "PUBLIC", then - consume those characters and switch to the before - DOCTYPE public identifier state. */ - $state = 'before DOCTYPE public identifier'; - - } elseif ($nextSix === 'SYSTEM') { - /* Otherwise, if the next six characters are an ASCII - case-insensitive match for the word "SYSTEM", then - consume those characters and switch to the before - DOCTYPE system identifier state. */ - $state = 'before DOCTYPE system identifier'; - - } else { - /* Otherwise, this is the parse error. Set the DOCTYPE - token's force-quirks flag to on. Switch to the bogus - DOCTYPE state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-space-or-right-bracket-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->token['error'] = true; - $state = 'bogus DOCTYPE'; - } - } - break; - - case 'before DOCTYPE public identifier': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before DOCTYPE public identifier state. */ - } elseif ($char === '"') { - /* U+0022 QUOTATION MARK (") - Set the DOCTYPE token's public identifier to the empty - string (not missing), then switch to the DOCTYPE public - identifier (double-quoted) state. */ - $this->token['public'] = ''; - $state = 'DOCTYPE public identifier (double-quoted)'; - } elseif ($char === "'") { - /* U+0027 APOSTROPHE (') - Set the DOCTYPE token's public identifier to the empty - string (not missing), then switch to the DOCTYPE public - identifier (single-quoted) state. */ - $this->token['public'] = ''; - $state = 'DOCTYPE public identifier (single-quoted)'; - } elseif ($char === '>') { - /* Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-end-of-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* Parse error. Set the DOCTYPE token's force-quirks - flag to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Parse error. Set the DOCTYPE token's force-quirks flag - to on. Switch to the bogus DOCTYPE state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-char-in-doctype' - )); - $this->token['force-quirks'] = true; - $state = 'bogus DOCTYPE'; - } - break; - - case 'DOCTYPE public identifier (double-quoted)': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the after DOCTYPE public identifier state. */ - $state = 'after DOCTYPE public identifier'; - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-end-of-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* EOF - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Anything else - Append the current input character to the current - DOCTYPE token's public identifier. Stay in the DOCTYPE - public identifier (double-quoted) state. */ - $this->token['public'] .= $char; - } - break; - - case 'DOCTYPE public identifier (single-quoted)': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "'") { - /* U+0027 APOSTROPHE (') - Switch to the after DOCTYPE public identifier state. */ - $state = 'after DOCTYPE public identifier'; - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-end-of-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* EOF - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Anything else - Append the current input character to the current - DOCTYPE token's public identifier. Stay in the DOCTYPE - public identifier (double-quoted) state. */ - $this->token['public'] .= $char; - } - break; - - case 'after DOCTYPE public identifier': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the after DOCTYPE public identifier state. */ - } elseif ($char === '"') { - /* U+0022 QUOTATION MARK (") - Set the DOCTYPE token's system identifier to the - empty string (not missing), then switch to the DOCTYPE - system identifier (double-quoted) state. */ - $this->token['system'] = ''; - $state = 'DOCTYPE system identifier (double-quoted)'; - } elseif ($char === "'") { - /* U+0027 APOSTROPHE (') - Set the DOCTYPE token's system identifier to the - empty string (not missing), then switch to the DOCTYPE - system identifier (single-quoted) state. */ - $this->token['system'] = ''; - $state = 'DOCTYPE system identifier (single-quoted)'; - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current DOCTYPE token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* Parse error. Set the DOCTYPE token's force-quirks - flag to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Anything else - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Switch to the bogus DOCTYPE state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-char-in-doctype' - )); - $this->token['force-quirks'] = true; - $state = 'bogus DOCTYPE'; - } - break; - - case 'before DOCTYPE system identifier': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before DOCTYPE system identifier state. */ - } elseif ($char === '"') { - /* U+0022 QUOTATION MARK (") - Set the DOCTYPE token's system identifier to the empty - string (not missing), then switch to the DOCTYPE system - identifier (double-quoted) state. */ - $this->token['system'] = ''; - $state = 'DOCTYPE system identifier (double-quoted)'; - } elseif ($char === "'") { - /* U+0027 APOSTROPHE (') - Set the DOCTYPE token's system identifier to the empty - string (not missing), then switch to the DOCTYPE system - identifier (single-quoted) state. */ - $this->token['system'] = ''; - $state = 'DOCTYPE system identifier (single-quoted)'; - } elseif ($char === '>') { - /* Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-char-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* Parse error. Set the DOCTYPE token's force-quirks - flag to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Parse error. Set the DOCTYPE token's force-quirks flag - to on. Switch to the bogus DOCTYPE state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-char-in-doctype' - )); - $this->token['force-quirks'] = true; - $state = 'bogus DOCTYPE'; - } - break; - - case 'DOCTYPE system identifier (double-quoted)': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the after DOCTYPE system identifier state. */ - $state = 'after DOCTYPE system identifier'; - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-end-of-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* EOF - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Anything else - Append the current input character to the current - DOCTYPE token's system identifier. Stay in the DOCTYPE - system identifier (double-quoted) state. */ - $this->token['system'] .= $char; - } - break; - - case 'DOCTYPE system identifier (single-quoted)': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "'") { - /* U+0027 APOSTROPHE (') - Switch to the after DOCTYPE system identifier state. */ - $state = 'after DOCTYPE system identifier'; - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-end-of-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* EOF - Parse error. Set the DOCTYPE token's force-quirks flag - to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Anything else - Append the current input character to the current - DOCTYPE token's system identifier. Stay in the DOCTYPE - system identifier (double-quoted) state. */ - $this->token['system'] .= $char; - } - break; - - case 'after DOCTYPE system identifier': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === "\t" || $char === "\n" || $char === "\x0c" || $char === ' ') { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the after DOCTYPE system identifier state. */ - } elseif ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current DOCTYPE token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - } elseif ($char === false) { - /* Parse error. Set the DOCTYPE token's force-quirks - flag to on. Emit that DOCTYPE token. Reconsume the EOF - character in the data state. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'eof-in-doctype' - )); - $this->token['force-quirks'] = true; - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - } else { - /* Anything else - Parse error. Switch to the bogus DOCTYPE state. - (This does not set the DOCTYPE token's force-quirks - flag to on.) */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'unexpected-char-in-doctype' - )); - $state = 'bogus DOCTYPE'; - } - break; - - case 'bogus DOCTYPE': - /* Consume the next input character: */ - $char = $this->stream->char(); - - if ($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the DOCTYPE token. Switch to the data state. */ - $this->emitToken($this->token); - $state = 'data'; - - } elseif ($char === false) { - /* EOF - Emit the DOCTYPE token. Reconsume the EOF character in - the data state. */ - $this->emitToken($this->token); - $this->stream->unget(); - $state = 'data'; - - } else { - /* Anything else - Stay in the bogus DOCTYPE state. */ - } - break; - - // case 'cdataSection': - } - } - } - - /** - * Returns a serialized representation of the tree. - * - * @return DOMDocument|DOMNodeList - */ - public function save() { - return $this->tree->save(); - } - - /** - * @return HTML5_TreeBuilder The tree - */ - public function getTree() - { - return $this->tree; - } - - - /** - * Returns the input stream. - * - * @return HTML5_InputStream - */ - public function stream() { - return $this->stream; - } - - /** - * @param bool $allowed - * @param bool $inattr - * @return string - */ - private function consumeCharacterReference($allowed = false, $inattr = false) { - // This goes quite far against spec, and is far closer to the Python - // impl., mainly because we don't do the large unconsuming the spec - // requires. - - // All consumed characters. - $chars = $this->stream->char(); - - /* This section defines how to consume a character - reference. This definition is used when parsing character - references in text and in attributes. - - The behavior depends on the identity of the next character - (the one immediately after the U+0026 AMPERSAND character): */ - - if ( - $chars[0] === "\x09" || - $chars[0] === "\x0A" || - $chars[0] === "\x0C" || - $chars[0] === "\x20" || - $chars[0] === '<' || - $chars[0] === '&' || - $chars === false || - $chars[0] === $allowed - ) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000C FORM FEED (FF) - U+0020 SPACE - U+003C LESS-THAN SIGN - U+0026 AMPERSAND - EOF - The additional allowed character, if there is one - Not a character reference. No characters are consumed, - and nothing is returned. (This is not an error, either.) */ - // We already consumed, so unconsume. - $this->stream->unget(); - return '&'; - } elseif ($chars[0] === '#') { - /* Consume the U+0023 NUMBER SIGN. */ - // Um, yeah, we already did that. - /* The behavior further depends on the character after - the U+0023 NUMBER SIGN: */ - $chars .= $this->stream->char(); - if (isset($chars[1]) && ($chars[1] === 'x' || $chars[1] === 'X')) { - /* U+0078 LATIN SMALL LETTER X - U+0058 LATIN CAPITAL LETTER X */ - /* Consume the X. */ - // Um, yeah, we already did that. - /* Follow the steps below, but using the range of - characters U+0030 DIGIT ZERO through to U+0039 DIGIT - NINE, U+0061 LATIN SMALL LETTER A through to U+0066 - LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER - A, through to U+0046 LATIN CAPITAL LETTER F (in other - words, 0123456789, ABCDEF, abcdef). */ - $char_class = self::HEX; - /* When it comes to interpreting the - number, interpret it as a hexadecimal number. */ - $hex = true; - } else { - /* Anything else */ - // Unconsume because we shouldn't have consumed this. - $chars = $chars[0]; - $this->stream->unget(); - /* Follow the steps below, but using the range of - characters U+0030 DIGIT ZERO through to U+0039 DIGIT - NINE (i.e. just 0123456789). */ - $char_class = self::DIGIT; - /* When it comes to interpreting the number, - interpret it as a decimal number. */ - $hex = false; - } - - /* Consume as many characters as match the range of characters given above. */ - $consumed = $this->stream->charsWhile($char_class); - if ($consumed === '' || $consumed === false) { - /* If no characters match the range, then don't consume - any characters (and unconsume the U+0023 NUMBER SIGN - character and, if appropriate, the X character). This - is a parse error; nothing is returned. */ - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-numeric-entity' - )); - return '&' . $chars; - } else { - /* Otherwise, if the next character is a U+003B SEMICOLON, - consume that too. If it isn't, there is a parse error. */ - if ($this->stream->char() !== ';') { - $this->stream->unget(); - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'numeric-entity-without-semicolon' - )); - } - - /* If one or more characters match the range, then take - them all and interpret the string of characters as a number - (either hexadecimal or decimal as appropriate). */ - $codepoint = $hex ? hexdec($consumed) : (int) $consumed; - - /* If that number is one of the numbers in the first column - of the following table, then this is a parse error. Find the - row with that number in the first column, and return a - character token for the Unicode character given in the - second column of that row. */ - $new_codepoint = HTML5_Data::getRealCodepoint($codepoint); - if ($new_codepoint) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'illegal-windows-1252-entity' - )); - return HTML5_Data::utf8chr($new_codepoint); - } else { - /* Otherwise, if the number is greater than 0x10FFFF, then - * this is a parse error. Return a U+FFFD REPLACEMENT - * CHARACTER. */ - if ($codepoint > 0x10FFFF) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'overlong-character-entity' // XXX probably not correct - )); - return "\xEF\xBF\xBD"; - } - /* Otherwise, return a character token for the Unicode - * character whose code point is that number. If the - * number is in the range 0x0001 to 0x0008, 0x000E to - * 0x001F, 0x007F to 0x009F, 0xD800 to 0xDFFF, 0xFDD0 to - * 0xFDEF, or is one of 0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, - * 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE, - * 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, - * 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, - * 0xAFFFF, 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, - * 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, - * or 0x10FFFF, then this is a parse error. */ - // && has higher precedence than || - if ( - $codepoint >= 0x0000 && $codepoint <= 0x0008 || - $codepoint === 0x000B || - $codepoint >= 0x000E && $codepoint <= 0x001F || - $codepoint >= 0x007F && $codepoint <= 0x009F || - $codepoint >= 0xD800 && $codepoint <= 0xDFFF || - $codepoint >= 0xFDD0 && $codepoint <= 0xFDEF || - ($codepoint & 0xFFFE) === 0xFFFE || - $codepoint == 0x10FFFF || $codepoint == 0x10FFFE - ) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'illegal-codepoint-for-numeric-entity' - )); - } - return HTML5_Data::utf8chr($codepoint); - } - } - } else { - /* Anything else */ - - /* Consume the maximum number of characters possible, - with the consumed characters matching one of the - identifiers in the first column of the named character - references table (in a case-sensitive manner). */ - // What we actually do here is consume as much as we can while it - // matches the start of one of the identifiers in the first column. - - $refs = HTML5_Data::getNamedCharacterReferences(); - - // Get the longest string which is the start of an identifier - // ($chars) as well as the longest identifier which matches ($id) - // and its codepoint ($codepoint). - $codepoint = false; - $char = $chars; - while ($char !== false && isset($refs[$char])) { - $refs = $refs[$char]; - if (isset($refs['codepoint'])) { - $id = $chars; - $codepoint = $refs['codepoint']; - } - $chars .= $char = $this->stream->char(); - } - - // Unconsume the one character we just took which caused the while - // statement to fail. This could be anything and could cause state - // changes (as if it matches the while loop it must be - // alphanumeric so we can just concat it to whatever we get later). - $this->stream->unget(); - if ($char !== false) { - $chars = substr($chars, 0, -1); - } - - /* If no match can be made, then this is a parse error. - No characters are consumed, and nothing is returned. */ - if (!$codepoint) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'expected-named-entity' - )); - return '&' . $chars; - } - - /* If the last character matched is not a U+003B SEMICOLON - (;), there is a parse error. */ - $semicolon = true; - if (substr($id, -1) !== ';') { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'named-entity-without-semicolon' - )); - $semicolon = false; - } - - /* If the character reference is being consumed as part of - an attribute, and the last character matched is not a - U+003B SEMICOLON (;), and the next character is in the - range U+0030 DIGIT ZERO to U+0039 DIGIT NINE, U+0041 - LATIN CAPITAL LETTER A to U+005A LATIN CAPITAL LETTER Z, - or U+0061 LATIN SMALL LETTER A to U+007A LATIN SMALL LETTER Z, - then, for historical reasons, all the characters that were - matched after the U+0026 AMPERSAND (&) must be unconsumed, - and nothing is returned. */ - if ($inattr && !$semicolon) { - // The next character is either the next character in $chars or in the stream. - if (strlen($chars) > strlen($id)) { - $next = substr($chars, strlen($id), 1); - } else { - $next = $this->stream->char(); - $this->stream->unget(); - } - if ( - '0' <= $next && $next <= '9' || - 'A' <= $next && $next <= 'Z' || - 'a' <= $next && $next <= 'z' - ) { - return '&' . $chars; - } - } - - /* Otherwise, return a character token for the character - corresponding to the character reference name (as given - by the second column of the named character references table). */ - return HTML5_Data::utf8chr($codepoint) . substr($chars, strlen($id)); - } - } - - /** - * @param bool $allowed - */ - private function characterReferenceInAttributeValue($allowed = false) { - /* Attempt to consume a character reference. */ - $entity = $this->consumeCharacterReference($allowed, true); - - /* If nothing is returned, append a U+0026 AMPERSAND - character to the current attribute's value. - - Otherwise, append the returned character token to the - current attribute's value. */ - $char = (!$entity) - ? '&' - : $entity; - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - /* Finally, switch back to the attribute value state that you - were in when were switched into this state. */ - } - - /** - * Emits a token, passing it on to the tree builder. - * - * @param $token - * @param bool $checkStream - * @param bool $dry - */ - protected function emitToken($token, $checkStream = true, $dry = false) { - if ($checkStream === true) { - // Emit errors from input stream. - while ($this->stream->errors) { - $this->emitToken(array_shift($this->stream->errors), false); - } - } - if ($token['type'] === self::ENDTAG && !empty($token['attr'])) { - for ($i = 0; $i < count($token['attr']); $i++) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'attributes-in-end-tag' - )); - } - } - if ($token['type'] === self::ENDTAG && !empty($token['self-closing'])) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'self-closing-flag-on-end-tag', - )); - } - if ($token['type'] === self::STARTTAG) { - // This could be changed to actually pass the tree-builder a hash - $hash = array(); - foreach ($token['attr'] as $keypair) { - if (isset($hash[$keypair['name']])) { - $this->emitToken(array( - 'type' => self::PARSEERROR, - 'data' => 'duplicate-attribute', - )); - } else { - $hash[$keypair['name']] = $keypair['value']; - } - } - } - - if ($dry === false) { - // the current structure of attributes is not a terribly good one - $this->tree->emitToken($token); - } - - if ($dry === false && is_int($this->tree->content_model)) { - $this->content_model = $this->tree->content_model; - $this->tree->content_model = null; - - } elseif ($token['type'] === self::ENDTAG) { - $this->content_model = self::PCDATA; - } - } -} - diff --git a/library/vendor/dompdf/lib/html5lib/TreeBuilder.php b/library/vendor/dompdf/lib/html5lib/TreeBuilder.php deleted file mode 100644 index cc8cbc6aa..000000000 --- a/library/vendor/dompdf/lib/html5lib/TreeBuilder.php +++ /dev/null @@ -1,3989 +0,0 @@ - -Copyright 2009 Edward Z. Yang - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -// Tags for FIX ME!!!: (in order of priority) -// XXX - should be fixed NAO! -// XERROR - with regards to parse errors -// XSCRIPT - with regards to scripting mode -// XENCODING - with regards to encoding (for reparsing tests) -// XDOM - DOM specific code (tagName is explicitly not marked). -// this is not (yet) in helper functions. - -class HTML5_TreeBuilder { - public $stack = array(); - public $content_model; - - private $mode; - private $original_mode; - private $secondary_mode; - private $dom; - // Whether or not normal insertion of nodes should actually foster - // parent (used in one case in spec) - private $foster_parent = false; - private $a_formatting = array(); - - private $head_pointer = null; - private $form_pointer = null; - - private $flag_frameset_ok = true; - private $flag_force_quirks = false; - private $ignored = false; - private $quirks_mode = null; - // this gets to 2 when we want to ignore the next lf character, and - // is decrement at the beginning of each processed token (this way, - // code can check for (bool)$ignore_lf_token, but it phases out - // appropriately) - private $ignore_lf_token = 0; - private $fragment = false; - private $root; - - private $scoping = array('applet','button','caption','html','marquee','object','table','td','th', 'svg:foreignObject'); - private $formatting = array('a','b','big','code','em','font','i','nobr','s','small','strike','strong','tt','u'); - // dl and ds are speculative - private $special = array('address','area','article','aside','base','basefont','bgsound', - 'blockquote','body','br','center','col','colgroup','command','dc','dd','details','dir','div','dl','ds', - 'dt','embed','fieldset','figure','footer','form','frame','frameset','h1','h2','h3','h4','h5', - 'h6','head','header','hgroup','hr','iframe','img','input','isindex','li','link', - 'listing','menu','meta','nav','noembed','noframes','noscript','ol', - 'p','param','plaintext','pre','script','select','spacer','style', - 'tbody','textarea','tfoot','thead','title','tr','ul','wbr'); - - private $pendingTableCharacters; - private $pendingTableCharactersDirty; - - // Tree construction modes - const INITIAL = 0; - const BEFORE_HTML = 1; - const BEFORE_HEAD = 2; - const IN_HEAD = 3; - const IN_HEAD_NOSCRIPT = 4; - const AFTER_HEAD = 5; - const IN_BODY = 6; - const IN_CDATA_RCDATA = 7; - const IN_TABLE = 8; - const IN_TABLE_TEXT = 9; - const IN_CAPTION = 10; - const IN_COLUMN_GROUP = 11; - const IN_TABLE_BODY = 12; - const IN_ROW = 13; - const IN_CELL = 14; - const IN_SELECT = 15; - const IN_SELECT_IN_TABLE= 16; - const IN_FOREIGN_CONTENT= 17; - const AFTER_BODY = 18; - const IN_FRAMESET = 19; - const AFTER_FRAMESET = 20; - const AFTER_AFTER_BODY = 21; - const AFTER_AFTER_FRAMESET = 22; - - /** - * Converts a magic number to a readable name. Use for debugging. - */ - private function strConst($number) { - static $lookup; - if (!$lookup) { - $lookup = array(); - $r = new ReflectionClass('HTML5_TreeBuilder'); - $consts = $r->getConstants(); - foreach ($consts as $const => $num) { - if (!is_int($num)) { - continue; - } - $lookup[$num] = $const; - } - } - return $lookup[$number]; - } - - // The different types of elements. - const SPECIAL = 100; - const SCOPING = 101; - const FORMATTING = 102; - const PHRASING = 103; - - // Quirks modes in $quirks_mode - const NO_QUIRKS = 200; - const QUIRKS_MODE = 201; - const LIMITED_QUIRKS_MODE = 202; - - // Marker to be placed in $a_formatting - const MARKER = 300; - - // Namespaces for foreign content - const NS_HTML = null; // to prevent DOM from requiring NS on everything - const NS_MATHML = 'http://www.w3.org/1998/Math/MathML'; - const NS_SVG = 'http://www.w3.org/2000/svg'; - const NS_XLINK = 'http://www.w3.org/1999/xlink'; - const NS_XML = 'http://www.w3.org/XML/1998/namespace'; - const NS_XMLNS = 'http://www.w3.org/2000/xmlns/'; - - // Different types of scopes to test for elements - const SCOPE = 0; - const SCOPE_LISTITEM = 1; - const SCOPE_TABLE = 2; - - /** - * HTML5_TreeBuilder constructor. - */ - public function __construct() { - $this->mode = self::INITIAL; - $this->dom = new DOMDocument; - - $this->dom->encoding = 'UTF-8'; - $this->dom->preserveWhiteSpace = true; - $this->dom->substituteEntities = true; - $this->dom->strictErrorChecking = false; - } - - public function getQuirksMode(){ - return $this->quirks_mode; - } - - /** - * Process tag tokens - * - * @param $token - * @param null $mode - */ - public function emitToken($token, $mode = null) { - // XXX: ignore parse errors... why are we emitting them, again? - if ($token['type'] === HTML5_Tokenizer::PARSEERROR) { - return; - } - if ($mode === null) { - $mode = $this->mode; - } - - /* - $backtrace = debug_backtrace(); - if ($backtrace[1]['class'] !== 'HTML5_TreeBuilder') echo "--\n"; - echo $this->strConst($mode); - if ($this->original_mode) echo " (originally ".$this->strConst($this->original_mode).")"; - echo "\n "; - token_dump($token); - $this->printStack(); - $this->printActiveFormattingElements(); - if ($this->foster_parent) echo " -> this is a foster parent mode\n"; - if ($this->flag_frameset_ok) echo " -> frameset ok\n"; - */ - - if ($this->ignore_lf_token) { - $this->ignore_lf_token--; - } - $this->ignored = false; - - switch ($mode) { - case self::INITIAL: - - /* A character token that is one of U+0009 CHARACTER TABULATION, - * U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020 SPACE */ - if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Ignore the token. */ - $this->ignored = true; - } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - if ( - $token['name'] !== 'html' || !empty($token['public']) || - !empty($token['system']) || $token !== 'about:legacy-compat' - ) { - /* If the DOCTYPE token's name is not a case-sensitive match - * for the string "html", or if the token's public identifier - * is not missing, or if the token's system identifier is - * neither missing nor a case-sensitive match for the string - * "about:legacy-compat", then there is a parse error (this - * is the DOCTYPE parse error). */ - // DOCTYPE parse error - } - /* Append a DocumentType node to the Document node, with the name - * attribute set to the name given in the DOCTYPE token, or the - * empty string if the name was missing; the publicId attribute - * set to the public identifier given in the DOCTYPE token, or - * the empty string if the public identifier was missing; the - * systemId attribute set to the system identifier given in the - * DOCTYPE token, or the empty string if the system identifier - * was missing; and the other attributes specific to - * DocumentType objects set to null and empty lists as - * appropriate. Associate the DocumentType node with the - * Document object so that it is returned as the value of the - * doctype attribute of the Document object. */ - if (!isset($token['public'])) { - $token['public'] = null; - } - if (!isset($token['system'])) { - $token['system'] = null; - } - // XDOM - // Yes this is hacky. I'm kind of annoyed that I can't appendChild - // a doctype to DOMDocument. Maybe I haven't chanted the right - // syllables. - $impl = new DOMImplementation(); - // This call can fail for particularly pathological cases (namely, - // the qualifiedName parameter ($token['name']) could be missing. - if ($token['name']) { - $doctype = $impl->createDocumentType($token['name'], $token['public'], $token['system']); - $this->dom->appendChild($doctype); - } else { - // It looks like libxml's not actually *able* to express this case. - // So... don't. - $this->dom->emptyDoctype = true; - } - $public = is_null($token['public']) ? false : strtolower($token['public']); - $system = is_null($token['system']) ? false : strtolower($token['system']); - $publicStartsWithForQuirks = array( - "+//silmaril//dtd html pro v0r11 19970101//", - "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", - "-//as//dtd html 3.0 aswedit + extensions//", - "-//ietf//dtd html 2.0 level 1//", - "-//ietf//dtd html 2.0 level 2//", - "-//ietf//dtd html 2.0 strict level 1//", - "-//ietf//dtd html 2.0 strict level 2//", - "-//ietf//dtd html 2.0 strict//", - "-//ietf//dtd html 2.0//", - "-//ietf//dtd html 2.1e//", - "-//ietf//dtd html 3.0//", - "-//ietf//dtd html 3.2 final//", - "-//ietf//dtd html 3.2//", - "-//ietf//dtd html 3//", - "-//ietf//dtd html level 0//", - "-//ietf//dtd html level 1//", - "-//ietf//dtd html level 2//", - "-//ietf//dtd html level 3//", - "-//ietf//dtd html strict level 0//", - "-//ietf//dtd html strict level 1//", - "-//ietf//dtd html strict level 2//", - "-//ietf//dtd html strict level 3//", - "-//ietf//dtd html strict//", - "-//ietf//dtd html//", - "-//metrius//dtd metrius presentational//", - "-//microsoft//dtd internet explorer 2.0 html strict//", - "-//microsoft//dtd internet explorer 2.0 html//", - "-//microsoft//dtd internet explorer 2.0 tables//", - "-//microsoft//dtd internet explorer 3.0 html strict//", - "-//microsoft//dtd internet explorer 3.0 html//", - "-//microsoft//dtd internet explorer 3.0 tables//", - "-//netscape comm. corp.//dtd html//", - "-//netscape comm. corp.//dtd strict html//", - "-//o'reilly and associates//dtd html 2.0//", - "-//o'reilly and associates//dtd html extended 1.0//", - "-//o'reilly and associates//dtd html extended relaxed 1.0//", - "-//spyglass//dtd html 2.0 extended//", - "-//sq//dtd html 2.0 hotmetal + extensions//", - "-//sun microsystems corp.//dtd hotjava html//", - "-//sun microsystems corp.//dtd hotjava strict html//", - "-//w3c//dtd html 3 1995-03-24//", - "-//w3c//dtd html 3.2 draft//", - "-//w3c//dtd html 3.2 final//", - "-//w3c//dtd html 3.2//", - "-//w3c//dtd html 3.2s draft//", - "-//w3c//dtd html 4.0 frameset//", - "-//w3c//dtd html 4.0 transitional//", - "-//w3c//dtd html experimental 19960712//", - "-//w3c//dtd html experimental 970421//", - "-//w3c//dtd w3 html//", - "-//w3o//dtd w3 html 3.0//", - "-//webtechs//dtd mozilla html 2.0//", - "-//webtechs//dtd mozilla html//", - ); - $publicSetToForQuirks = array( - "-//w3o//dtd w3 html strict 3.0//", - "-/w3c/dtd html 4.0 transitional/en", - "html", - ); - $publicStartsWithAndSystemForQuirks = array( - "-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//", - ); - $publicStartsWithForLimitedQuirks = array( - "-//w3c//dtd xhtml 1.0 frameset//", - "-//w3c//dtd xhtml 1.0 transitional//", - ); - $publicStartsWithAndSystemForLimitedQuirks = array( - "-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//", - ); - // first, do easy checks - if ( - !empty($token['force-quirks']) || - strtolower($token['name']) !== 'html' - ) { - $this->quirks_mode = self::QUIRKS_MODE; - } else { - do { - if ($system) { - foreach ($publicStartsWithAndSystemForQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - } - if (!is_null($this->quirks_mode)) { - break; - } - foreach ($publicStartsWithAndSystemForLimitedQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::LIMITED_QUIRKS_MODE; - break; - } - } - if (!is_null($this->quirks_mode)) { - break; - } - } - foreach ($publicSetToForQuirks as $x) { - if ($public === $x) { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - } - if (!is_null($this->quirks_mode)) { - break; - } - foreach ($publicStartsWithForLimitedQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::LIMITED_QUIRKS_MODE; - } - } - if (!is_null($this->quirks_mode)) { - break; - } - if ($system === "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - foreach ($publicStartsWithForQuirks as $x) { - if (strncmp($public, $x, strlen($x)) === 0) { - $this->quirks_mode = self::QUIRKS_MODE; - break; - } - } - if (is_null($this->quirks_mode)) { - $this->quirks_mode = self::NO_QUIRKS; - } - } while (false); - } - $this->mode = self::BEFORE_HTML; - } else { - // parse error - /* Switch the insertion mode to "before html", then reprocess the - * current token. */ - $this->mode = self::BEFORE_HTML; - $this->quirks_mode = self::QUIRKS_MODE; - $this->emitToken($token); - } - break; - - case self::BEFORE_HTML: - /* A DOCTYPE token */ - if ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // Parse error. Ignore the token. - $this->ignored = true; - - /* A comment token */ - } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - // XDOM - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Ignore the token. */ - $this->ignored = true; - - /* A start tag whose tag name is "html" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] == 'html') { - /* Create an element for the token in the HTML namespace. Append it - * to the Document object. Put this element in the stack of open - * elements. */ - // XDOM - $html = $this->insertElement($token, false); - $this->dom->appendChild($html); - $this->stack[] = $html; - - $this->mode = self::BEFORE_HEAD; - - } else { - /* Create an html element. Append it to the Document object. Put - * this element in the stack of open elements. */ - // XDOM - $html = $this->dom->createElementNS(self::NS_HTML, 'html'); - $this->dom->appendChild($html); - $this->stack[] = $html; - - /* Switch the insertion mode to "before head", then reprocess the - * current token. */ - $this->mode = self::BEFORE_HEAD; - $this->emitToken($token); - } - break; - - case self::BEFORE_HEAD: - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Ignore the token. */ - $this->ignored = true; - - /* A comment token */ - } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A DOCTYPE token */ - } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - /* Parse error. Ignore the token */ - $this->ignored = true; - // parse error - - /* A start tag token with the tag name "html" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - /* Process the token using the rules for the "in body" - * insertion mode. */ - $this->processWithRulesFor($token, self::IN_BODY); - - /* A start tag token with the tag name "head" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') { - /* Insert an HTML element for the token. */ - $element = $this->insertElement($token); - - /* Set the head element pointer to this new element node. */ - $this->head_pointer = $element; - - /* Change the insertion mode to "in head". */ - $this->mode = self::IN_HEAD; - - /* An end tag whose tag name is one of: "head", "body", "html", "br" */ - } elseif ( - $token['type'] === HTML5_Tokenizer::ENDTAG && ( - $token['name'] === 'head' || $token['name'] === 'body' || - $token['name'] === 'html' || $token['name'] === 'br' - )) { - /* Act as if a start tag token with the tag name "head" and no - * attributes had been seen, then reprocess the current token. */ - $this->emitToken(array( - 'name' => 'head', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - $this->emitToken($token); - - /* Any other end tag */ - } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) { - /* Parse error. Ignore the token. */ - $this->ignored = true; - - } else { - /* Act as if a start tag token with the tag name "head" and no - * attributes had been seen, then reprocess the current token. - * Note: This will result in an empty head element being - * generated, with the current token being reprocessed in the - * "after head" insertion mode. */ - $this->emitToken(array( - 'name' => 'head', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - $this->emitToken($token); - } - break; - - case self::IN_HEAD: - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. */ - if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Insert the character into the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A DOCTYPE token */ - } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - /* Parse error. Ignore the token. */ - $this->ignored = true; - // parse error - - /* A start tag whose tag name is "html" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && - $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - - /* A start tag whose tag name is one of: "base", "command", "link" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'base' || $token['name'] === 'command' || - $token['name'] === 'link')) { - /* Insert an HTML element for the token. Immediately pop the - * current node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - - /* A start tag whose tag name is "meta" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'meta') { - /* Insert an HTML element for the token. Immediately pop the - * current node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - - // XERROR: Acknowledge the token's self-closing flag, if it is set. - - // XENCODING: If the element has a charset attribute, and its value is a - // supported encoding, and the confidence is currently tentative, - // then change the encoding to the encoding given by the value of - // the charset attribute. - // - // Otherwise, if the element has a content attribute, and applying - // the algorithm for extracting an encoding from a Content-Type to - // its value returns a supported encoding encoding, and the - // confidence is currently tentative, then change the encoding to - // the encoding encoding. - - /* A start tag with the tag name "title" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'title') { - $this->insertRCDATAElement($token); - - /* A start tag whose tag name is "noscript", if the scripting flag is enabled, or - * A start tag whose tag name is one of: "noframes", "style" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && - ($token['name'] === 'noscript' || $token['name'] === 'noframes' || $token['name'] === 'style')) { - // XSCRIPT: Scripting flag not respected - $this->insertCDATAElement($token); - - // XSCRIPT: Scripting flag disable not implemented - - /* A start tag with the tag name "script" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') { - /* 1. Create an element for the token in the HTML namespace. */ - $node = $this->insertElement($token, false); - - /* 2. Mark the element as being "parser-inserted" */ - // Uhhh... XSCRIPT - - /* 3. If the parser was originally created for the HTML - * fragment parsing algorithm, then mark the script element as - * "already executed". (fragment case) */ - // ditto... XSCRIPT - - /* 4. Append the new element to the current node and push it onto - * the stack of open elements. */ - end($this->stack)->appendChild($node); - $this->stack[] = $node; - // I guess we could squash these together - - /* 6. Let the original insertion mode be the current insertion mode. */ - $this->original_mode = $this->mode; - /* 7. Switch the insertion mode to "in CDATA/RCDATA" */ - $this->mode = self::IN_CDATA_RCDATA; - /* 5. Switch the tokeniser's content model flag to the CDATA state. */ - $this->content_model = HTML5_Tokenizer::CDATA; - - /* An end tag with the tag name "head" */ - } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'head') { - /* Pop the current node (which will be the head element) off the stack of open elements. */ - array_pop($this->stack); - - /* Change the insertion mode to "after head". */ - $this->mode = self::AFTER_HEAD; - - // Slight logic inversion here to minimize duplication - /* A start tag with the tag name "head". */ - /* An end tag whose tag name is not one of: "body", "html", "br" */ - } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') || - ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] !== 'html' && - $token['name'] !== 'body' && $token['name'] !== 'br')) { - // Parse error. Ignore the token. - $this->ignored = true; - - /* Anything else */ - } else { - /* Act as if an end tag token with the tag name "head" had been - * seen, and reprocess the current token. */ - $this->emitToken(array( - 'name' => 'head', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - /* Then, reprocess the current token. */ - $this->emitToken($token); - } - break; - - case self::IN_HEAD_NOSCRIPT: - if ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'noscript') { - /* Pop the current node (which will be a noscript element) from the - * stack of open elements; the new current node will be a head - * element. */ - array_pop($this->stack); - $this->mode = self::IN_HEAD; - } elseif ( - ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) || - ($token['type'] === HTML5_Tokenizer::COMMENT) || - ($token['type'] === HTML5_Tokenizer::STARTTAG && ( - $token['name'] === 'link' || $token['name'] === 'meta' || - $token['name'] === 'noframes' || $token['name'] === 'style'))) { - $this->processWithRulesFor($token, self::IN_HEAD); - // inverted logic - } elseif ( - ($token['type'] === HTML5_Tokenizer::STARTTAG && ( - $token['name'] === 'head' || $token['name'] === 'noscript')) || - ($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] !== 'br')) { - // parse error - } else { - // parse error - $this->emitToken(array( - 'type' => HTML5_Tokenizer::ENDTAG, - 'name' => 'noscript', - )); - $this->emitToken($token); - } - break; - - case self::AFTER_HEAD: - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) { - // parse error - - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') { - $this->processWithRulesFor($token, self::IN_BODY); - - /* A start tag token with the tag name "body" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'body') { - $this->insertElement($token); - - /* Set the frameset-ok flag to "not ok". */ - $this->flag_frameset_ok = false; - - /* Change the insertion mode to "in body". */ - $this->mode = self::IN_BODY; - - /* A start tag token with the tag name "frameset" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'frameset') { - /* Insert a frameset element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in frameset". */ - $this->mode = self::IN_FRAMESET; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title" */ - } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'], - array('base', 'link', 'meta', 'noframes', 'script', 'style', 'title'))) { - // parse error - /* Push the node pointed to by the head element pointer onto the - * stack of open elements. */ - $this->stack[] = $this->head_pointer; - $this->processWithRulesFor($token, self::IN_HEAD); - array_splice($this->stack, array_search($this->head_pointer, $this->stack, true), 1); - - // inversion of specification - } elseif ( - ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'head') || - ($token['type'] === HTML5_Tokenizer::ENDTAG && - $token['name'] !== 'body' && $token['name'] !== 'html' && - $token['name'] !== 'br')) { - // parse error - - /* Anything else */ - } else { - $this->emitToken(array( - 'name' => 'body', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - $this->flag_frameset_ok = true; - $this->emitToken($token); - } - break; - - case self::IN_BODY: - /* Handle the token as follows: */ - - switch($token['type']) { - /* A character token */ - case HTML5_Tokenizer::CHARACTER: - case HTML5_Tokenizer::SPACECHARACTER: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - - /* If the token is not one of U+0009 CHARACTER TABULATION, - * U+000A LINE FEED (LF), U+000C FORM FEED (FF), or U+0020 - * SPACE, then set the frameset-ok flag to "not ok". */ - // i.e., if any of the characters is not whitespace - if (strlen($token['data']) !== strspn($token['data'], HTML5_Tokenizer::WHITESPACE)) { - $this->flag_frameset_ok = false; - } - break; - - /* A comment token */ - case HTML5_Tokenizer::COMMENT: - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - break; - - case HTML5_Tokenizer::DOCTYPE: - // parse error - break; - - case HTML5_Tokenizer::EOF: - // parse error - break; - - case HTML5_Tokenizer::STARTTAG: - switch($token['name']) { - case 'html': - // parse error - /* For each attribute on the token, check to see if the - * attribute is already present on the top element of the - * stack of open elements. If it is not, add the attribute - * and its corresponding value to that element. */ - foreach($token['attr'] as $attr) { - if (!$this->stack[0]->hasAttribute($attr['name'])) { - $this->stack[0]->setAttribute($attr['name'], $attr['value']); - } - } - break; - - case 'base': case 'command': case 'link': case 'meta': case 'noframes': - case 'script': case 'style': case 'title': - /* Process the token as if the insertion mode had been "in - head". */ - $this->processWithRulesFor($token, self::IN_HEAD); - break; - - /* A start tag token with the tag name "body" */ - case 'body': - /* Parse error. If the second element on the stack of open - elements is not a body element, or, if the stack of open - elements has only one node on it, then ignore the token. - (fragment case) */ - if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') { - $this->ignored = true; - // Ignore - - /* Otherwise, for each attribute on the token, check to see - if the attribute is already present on the body element (the - second element) on the stack of open elements. If it is not, - add the attribute and its corresponding value to that - element. */ - } else { - foreach($token['attr'] as $attr) { - if (!$this->stack[1]->hasAttribute($attr['name'])) { - $this->stack[1]->setAttribute($attr['name'], $attr['value']); - } - } - } - break; - - case 'frameset': - // parse error - /* If the second element on the stack of open elements is - * not a body element, or, if the stack of open elements - * has only one node on it, then ignore the token. - * (fragment case) */ - if (count($this->stack) === 1 || $this->stack[1]->tagName !== 'body') { - $this->ignored = true; - // Ignore - } elseif (!$this->flag_frameset_ok) { - $this->ignored = true; - // Ignore - } else { - /* 1. Remove the second element on the stack of open - * elements from its parent node, if it has one. */ - if ($this->stack[1]->parentNode) { - $this->stack[1]->parentNode->removeChild($this->stack[1]); - } - - /* 2. Pop all the nodes from the bottom of the stack of - * open elements, from the current node up to the root - * html element. */ - array_splice($this->stack, 1); - - $this->insertElement($token); - $this->mode = self::IN_FRAMESET; - } - break; - - // in spec, there is a diversion here - - case 'address': case 'article': case 'aside': case 'blockquote': - case 'center': case 'datagrid': case 'details': case 'dir': - case 'div': case 'dl': case 'fieldset': case 'figure': case 'footer': - case 'header': case 'hgroup': case 'menu': case 'nav': - case 'ol': case 'p': case 'section': case 'ul': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if ($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* If the current node is an element whose tag name is one - * of "h1", "h2", "h3", "h4", "h5", or "h6", then this is a - * parse error; pop the current node off the stack of open - * elements. */ - $peek = array_pop($this->stack); - if (in_array($peek->tagName, array("h1", "h2", "h3", "h4", "h5", "h6"))) { - // parse error - } else { - $this->stack[] = $peek; - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - case 'pre': case 'listing': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - $this->insertElement($token); - /* If the next token is a U+000A LINE FEED (LF) character - * token, then ignore that token and move on to the next - * one. (Newlines at the start of pre blocks are ignored as - * an authoring convenience.) */ - $this->ignore_lf_token = 2; - $this->flag_frameset_ok = false; - break; - - /* A start tag whose tag name is "form" */ - case 'form': - /* If the form element pointer is not null, ignore the - token with a parse error. */ - if ($this->form_pointer !== null) { - $this->ignored = true; - // Ignore. - - /* Otherwise: */ - } else { - /* If the stack of open elements has a p element in - scope, then act as if an end tag with the tag name p - had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token, and set the - form element pointer to point to the element created. */ - $element = $this->insertElement($token); - $this->form_pointer = $element; - } - break; - - // condensed specification - case 'li': case 'dc': case 'dd': case 'ds': case 'dt': - /* 1. Set the frameset-ok flag to "not ok". */ - $this->flag_frameset_ok = false; - - $stack_length = count($this->stack) - 1; - for($n = $stack_length; 0 <= $n; $n--) { - /* 2. Initialise node to be the current node (the - bottommost node of the stack). */ - $stop = false; - $node = $this->stack[$n]; - $cat = $this->getElementCategory($node); - - // for case 'li': - /* 3. If node is an li element, then act as if an end - * tag with the tag name "li" had been seen, then jump - * to the last step. */ - // for case 'dc': case 'dd': case 'ds': case 'dt': - /* If node is a dc, dd, ds or dt element, then act as if an end - * tag with the same tag name as node had been seen, then - * jump to the last step. */ - if (($token['name'] === 'li' && $node->tagName === 'li') || - ($token['name'] !== 'li' && ($node->tagName == 'dc' || $node->tagName === 'dd' || $node->tagName == 'ds' || $node->tagName === 'dt'))) { // limited conditional - $this->emitToken(array( - 'type' => HTML5_Tokenizer::ENDTAG, - 'name' => $node->tagName, - )); - break; - } - - /* 4. If node is not in the formatting category, and is - not in the phrasing category, and is not an address, - div or p element, then stop this algorithm. */ - if ($cat !== self::FORMATTING && $cat !== self::PHRASING && - $node->tagName !== 'address' && $node->tagName !== 'div' && - $node->tagName !== 'p') { - break; - } - - /* 5. Otherwise, set node to the previous entry in the - * stack of open elements and return to step 2. */ - } - - /* 6. This is the last step. */ - - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if ($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Finally, insert an HTML element with the same tag - name as the token's. */ - $this->insertElement($token); - break; - - /* A start tag token whose tag name is "plaintext" */ - case 'plaintext': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if ($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - $this->content_model = HTML5_Tokenizer::PLAINTEXT; - break; - - // more diversions - - /* A start tag whose tag name is "a" */ - case 'a': - /* If the list of active formatting elements contains - an element whose tag name is "a" between the end of the - list and the last marker on the list (or the start of - the list if there is no marker on the list), then this - is a parse error; act as if an end tag with the tag name - "a" had been seen, then remove that element from the list - of active formatting elements and the stack of open - elements if the end tag didn't already remove it (it - might not have if the element is not in table scope). */ - $leng = count($this->a_formatting); - - for ($n = $leng - 1; $n >= 0; $n--) { - if ($this->a_formatting[$n] === self::MARKER) { - break; - - } elseif ($this->a_formatting[$n]->tagName === 'a') { - $a = $this->a_formatting[$n]; - $this->emitToken(array( - 'name' => 'a', - 'type' => HTML5_Tokenizer::ENDTAG - )); - if (in_array($a, $this->a_formatting)) { - $a_i = array_search($a, $this->a_formatting, true); - if ($a_i !== false) { - array_splice($this->a_formatting, $a_i, 1); - } - } - if (in_array($a, $this->stack)) { - $a_i = array_search($a, $this->stack, true); - if ($a_i !== false) { - array_splice($this->stack, $a_i, 1); - } - } - break; - } - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - case 'b': case 'big': case 'code': case 'em': case 'font': case 'i': - case 's': case 'small': case 'strike': - case 'strong': case 'tt': case 'u': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - case 'nobr': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* If the stack of open elements has a nobr element in - * scope, then this is a parse error; act as if an end tag - * with the tag name "nobr" had been seen, then once again - * reconstruct the active formatting elements, if any. */ - if ($this->elementInScope('nobr')) { - $this->emitToken(array( - 'name' => 'nobr', - 'type' => HTML5_Tokenizer::ENDTAG, - )); - $this->reconstructActiveFormattingElements(); - } - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - // another diversion - - /* A start tag token whose tag name is "button" */ - case 'button': - /* If the stack of open elements has a button element in scope, - then this is a parse error; act as if an end tag with the tag - name "button" had been seen, then reprocess the token. (We don't - do that. Unnecessary.) (I hope you're right! -- ezyang) */ - if ($this->elementInScope('button')) { - $this->emitToken(array( - 'name' => 'button', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - $this->flag_frameset_ok = false; - break; - - case 'applet': case 'marquee': case 'object': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - $this->flag_frameset_ok = false; - break; - - // spec diversion - - /* A start tag whose tag name is "table" */ - case 'table': - /* If the Document is not set to quirks mode, and the - * stack of open elements has a p element in scope, then - * act as if an end tag with the tag name "p" had been - * seen. */ - if ($this->quirks_mode !== self::QUIRKS_MODE && - $this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - $this->flag_frameset_ok = false; - - /* Change the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - break; - - /* A start tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ - case 'area': case 'basefont': case 'bgsound': case 'br': - case 'embed': case 'img': case 'input': case 'keygen': case 'spacer': - case 'wbr': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - - $this->flag_frameset_ok = false; - break; - - case 'param': case 'source': - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - break; - - /* A start tag whose tag name is "hr" */ - case 'hr': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if ($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - // YYY: Acknowledge the token's self-closing flag, if it is set. - - $this->flag_frameset_ok = false; - break; - - /* A start tag whose tag name is "image" */ - case 'image': - /* Parse error. Change the token's tag name to "img" and - reprocess it. (Don't ask.) */ - $token['name'] = 'img'; - $this->emitToken($token); - break; - - /* A start tag whose tag name is "isindex" */ - case 'isindex': - /* Parse error. */ - - /* If the form element pointer is not null, - then ignore the token. */ - if ($this->form_pointer === null) { - /* Act as if a start tag token with the tag name "form" had - been seen. */ - /* If the token has an attribute called "action", set - * the action attribute on the resulting form - * element to the value of the "action" attribute of - * the token. */ - $attr = array(); - $action = $this->getAttr($token, 'action'); - if ($action !== false) { - $attr[] = array('name' => 'action', 'value' => $action); - } - $this->emitToken(array( - 'name' => 'form', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => $attr - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->emitToken(array( - 'name' => 'hr', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "label" - had been seen. */ - $this->emitToken(array( - 'name' => 'label', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => array() - )); - - /* Act as if a stream of character tokens had been seen. */ - $prompt = $this->getAttr($token, 'prompt'); - if ($prompt === false) { - $prompt = 'This is a searchable index. '. - 'Insert your search keywords here: '; - } - $this->emitToken(array( - 'data' => $prompt, - 'type' => HTML5_Tokenizer::CHARACTER, - )); - - /* Act as if a start tag token with the tag name "input" - had been seen, with all the attributes from the "isindex" - token, except with the "name" attribute set to the value - "isindex" (ignoring any explicit "name" attribute). */ - $attr = array(); - foreach ($token['attr'] as $keypair) { - if ($keypair['name'] === 'name' || $keypair['name'] === 'action' || - $keypair['name'] === 'prompt') { - continue; - } - $attr[] = $keypair; - } - $attr[] = array('name' => 'name', 'value' => 'isindex'); - - $this->emitToken(array( - 'name' => 'input', - 'type' => HTML5_Tokenizer::STARTTAG, - 'attr' => $attr - )); - - /* Act as if an end tag token with the tag name "label" - had been seen. */ - $this->emitToken(array( - 'name' => 'label', - 'type' => HTML5_Tokenizer::ENDTAG - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->emitToken(array( - 'name' => 'hr', - 'type' => HTML5_Tokenizer::STARTTAG - )); - - /* Act as if an end tag token with the tag name "form" had - been seen. */ - $this->emitToken(array( - 'name' => 'form', - 'type' => HTML5_Tokenizer::ENDTAG - )); - } else { - $this->ignored = true; - } - break; - - /* A start tag whose tag name is "textarea" */ - case 'textarea': - $this->insertElement($token); - - /* If the next token is a U+000A LINE FEED (LF) - * character token, then ignore that token and move on to - * the next one. (Newlines at the start of textarea - * elements are ignored as an authoring convenience.) - * need flag, see also
 */
-                            $this->ignore_lf_token = 2;
-
-                            $this->original_mode = $this->mode;
-                            $this->flag_frameset_ok = false;
-                            $this->mode = self::IN_CDATA_RCDATA;
-
-                            /* Switch the tokeniser's content model flag to the
-                            RCDATA state. */
-                            $this->content_model = HTML5_Tokenizer::RCDATA;
-                        break;
-
-                        /* A start tag token whose tag name is "xmp" */
-                        case 'xmp':
-                            /* If the stack of open elements has a p element in
-                            scope, then act as if an end tag with the tag name
-                            "p" has been seen. */
-                            if ($this->elementInScope('p')) {
-                                $this->emitToken(array(
-                                    'name' => 'p',
-                                    'type' => HTML5_Tokenizer::ENDTAG
-                                ));
-                            }
-
-                            /* Reconstruct the active formatting elements, if any. */
-                            $this->reconstructActiveFormattingElements();
-
-                            $this->flag_frameset_ok = false;
-
-                            $this->insertCDATAElement($token);
-                        break;
-
-                        case 'iframe':
-                            $this->flag_frameset_ok = false;
-                            $this->insertCDATAElement($token);
-                        break;
-
-                        case 'noembed': case 'noscript':
-                            // XSCRIPT: should check scripting flag
-                            $this->insertCDATAElement($token);
-                        break;
-
-                        /* A start tag whose tag name is "select" */
-                        case 'select':
-                            /* Reconstruct the active formatting elements, if any. */
-                            $this->reconstructActiveFormattingElements();
-
-                            /* Insert an HTML element for the token. */
-                            $this->insertElement($token);
-
-                            $this->flag_frameset_ok = false;
-
-                            /* If the insertion mode is one of in table", "in caption",
-                             * "in column group", "in table body", "in row", or "in
-                             * cell", then switch the insertion mode to "in select in
-                             * table". Otherwise, switch the insertion mode  to "in
-                             * select". */
-                            if (
-                                $this->mode === self::IN_TABLE || $this->mode === self::IN_CAPTION ||
-                                $this->mode === self::IN_COLUMN_GROUP || $this->mode ==+self::IN_TABLE_BODY ||
-                                $this->mode === self::IN_ROW || $this->mode === self::IN_CELL
-                            ) {
-                                $this->mode = self::IN_SELECT_IN_TABLE;
-                            } else {
-                                $this->mode = self::IN_SELECT;
-                            }
-                        break;
-
-                        case 'option': case 'optgroup':
-                            if ($this->elementInScope('option')) {
-                                $this->emitToken(array(
-                                    'name' => 'option',
-                                    'type' => HTML5_Tokenizer::ENDTAG,
-                                ));
-                            }
-                            $this->reconstructActiveFormattingElements();
-                            $this->insertElement($token);
-                        break;
-
-                        case 'rp': case 'rt':
-                            /* If the stack of open elements has a ruby element in scope, then generate
-                             * implied end tags. If the current node is not then a ruby element, this is
-                             * a parse error; pop all the nodes from the current node up to the node
-                             * immediately before the bottommost ruby element on the stack of open elements.
-                             */
-                            if ($this->elementInScope('ruby')) {
-                                $this->generateImpliedEndTags();
-                            }
-                            $peek = false;
-                            do {
-                                /*if ($peek) {
-                                    // parse error
-                                }*/
-                                $peek = array_pop($this->stack);
-                            } while ($peek->tagName !== 'ruby');
-                            $this->stack[] = $peek; // we popped one too many
-                            $this->insertElement($token);
-                        break;
-
-                        // spec diversion
-
-                        case 'math':
-                            $this->reconstructActiveFormattingElements();
-                            $token = $this->adjustMathMLAttributes($token);
-                            $token = $this->adjustForeignAttributes($token);
-                            $this->insertForeignElement($token, self::NS_MATHML);
-                            if (isset($token['self-closing'])) {
-                                // XERROR: acknowledge the token's self-closing flag
-                                array_pop($this->stack);
-                            }
-                            if ($this->mode !== self::IN_FOREIGN_CONTENT) {
-                                $this->secondary_mode = $this->mode;
-                                $this->mode = self::IN_FOREIGN_CONTENT;
-                            }
-                        break;
-
-                        case 'svg':
-                            $this->reconstructActiveFormattingElements();
-                            $token = $this->adjustSVGAttributes($token);
-                            $token = $this->adjustForeignAttributes($token);
-                            $this->insertForeignElement($token, self::NS_SVG);
-                            if (isset($token['self-closing'])) {
-                                // XERROR: acknowledge the token's self-closing flag
-                                array_pop($this->stack);
-                            }
-                            if ($this->mode !== self::IN_FOREIGN_CONTENT) {
-                                $this->secondary_mode = $this->mode;
-                                $this->mode = self::IN_FOREIGN_CONTENT;
-                            }
-                        break;
-
-                        case 'caption': case 'col': case 'colgroup': case 'frame': case 'head':
-                        case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': case 'tr':
-                            // parse error
-                        break;
-
-                        /* A start tag token not covered by the previous entries */
-                        default:
-                            /* Reconstruct the active formatting elements, if any. */
-                            $this->reconstructActiveFormattingElements();
-
-                            $this->insertElement($token);
-                            /* This element will be a phrasing  element. */
-                        break;
-                    }
-                    break;
-
-                    case HTML5_Tokenizer::ENDTAG:
-                    switch ($token['name']) {
-                        /* An end tag with the tag name "body" */
-                        case 'body':
-                            /* If the stack of open elements does not have a body
-                             * element in scope, this is a parse error; ignore the
-                             * token. */
-                            if (!$this->elementInScope('body')) {
-                                $this->ignored = true;
-
-                            /* Otherwise, if there is a node in the stack of open
-                             * elements that is not either a dc element, a dd element,
-                             * a ds element, a dt element, an li element, an optgroup
-                             * element, an option element, a p element, an rp element,
-                             * an rt element, a tbody element, a td element, a tfoot
-                             * element, a th element, a thead element, a tr element,
-                             * the body element, or the html element, then this is a
-                             * parse error.
-                             */
-                            } else {
-                                // XERROR: implement this check for parse error
-                            }
-
-                            /* Change the insertion mode to "after body". */
-                            $this->mode = self::AFTER_BODY;
-                        break;
-
-                        /* An end tag with the tag name "html" */
-                        case 'html':
-                            /* Act as if an end tag with tag name "body" had been seen,
-                            then, if that token wasn't ignored, reprocess the current
-                            token. */
-                            $this->emitToken(array(
-                                'name' => 'body',
-                                'type' => HTML5_Tokenizer::ENDTAG
-                            ));
-
-                            if (!$this->ignored) {
-                                $this->emitToken($token);
-                            }
-                        break;
-
-                        case 'address': case 'article': case 'aside': case 'blockquote':
-                        case 'center': case 'datagrid': case 'details': case 'dir':
-                        case 'div': case 'dl': case 'fieldset': case 'footer':
-                        case 'header': case 'hgroup': case 'listing': case 'menu':
-                        case 'nav': case 'ol': case 'pre': case 'section': case 'ul':
-                            /* If the stack of open elements has an element in scope
-                            with the same tag name as that of the token, then generate
-                            implied end tags. */
-                            if ($this->elementInScope($token['name'])) {
-                                $this->generateImpliedEndTags();
-
-                                /* Now, if the current node is not an element with
-                                the same tag name as that of the token, then this
-                                is a parse error. */
-                                // XERROR: implement parse error logic
-
-                                /* If the stack of open elements has an element in
-                                scope with the same tag name as that of the token,
-                                then pop elements from this stack until an element
-                                with that tag name has been popped from the stack. */
-                                do {
-                                    $node = array_pop($this->stack);
-                                } while ($node->tagName !== $token['name']);
-                            } else {
-                                // parse error
-                            }
-                        break;
-
-                        /* An end tag whose tag name is "form" */
-                        case 'form':
-                            /* Let node be the element that the form element pointer is set to. */
-                            $node = $this->form_pointer;
-                            /* Set the form element pointer  to null. */
-                            $this->form_pointer = null;
-                            /* If node is null or the stack of open elements does not
-                                * have node in scope, then this is a parse error; ignore the token. */
-                            if ($node === null || !in_array($node, $this->stack)) {
-                                // parse error
-                                $this->ignored = true;
-                            } else {
-                                /* 1. Generate implied end tags. */
-                                $this->generateImpliedEndTags();
-                                /* 2. If the current node is not node, then this is a parse error.  */
-                                if (end($this->stack) !== $node) {
-                                    // parse error
-                                }
-                                /* 3. Remove node from the stack of open elements. */
-                                array_splice($this->stack, array_search($node, $this->stack, true), 1);
-                            }
-
-                        break;
-
-                        /* An end tag whose tag name is "p" */
-                        case 'p':
-                            /* If the stack of open elements has a p element in scope,
-                            then generate implied end tags, except for p elements. */
-                            if ($this->elementInScope('p')) {
-                                /* Generate implied end tags, except for elements with
-                                 * the same tag name as the token. */
-                                $this->generateImpliedEndTags(array('p'));
-
-                                /* If the current node is not a p element, then this is
-                                a parse error. */
-                                // XERROR: implement
-
-                                /* Pop elements from the stack of open elements  until
-                                 * an element with the same tag name as the token has
-                                 * been popped from the stack. */
-                                do {
-                                    $node = array_pop($this->stack);
-                                } while ($node->tagName !== 'p');
-
-                            } else {
-                                // parse error
-                                $this->emitToken(array(
-                                    'name' => 'p',
-                                    'type' => HTML5_Tokenizer::STARTTAG,
-                                ));
-                                $this->emitToken($token);
-                            }
-                        break;
-
-                        /* An end tag whose tag name is "li" */
-                        case 'li':
-                            /* If the stack of open elements does not have an element
-                             * in list item scope with the same tag name as that of the
-                             * token, then this is a parse error; ignore the token. */
-                            if ($this->elementInScope($token['name'], self::SCOPE_LISTITEM)) {
-                                /* Generate implied end tags, except for elements with the
-                                 * same tag name as the token. */
-                                $this->generateImpliedEndTags(array($token['name']));
-                                /* If the current node is not an element with the same tag
-                                 * name as that of the token, then this is a parse error. */
-                                // XERROR: parse error
-                                /* Pop elements from the stack of open elements  until an
-                                 * element with the same tag name as the token has been
-                                 * popped from the stack. */
-                                do {
-                                    $node = array_pop($this->stack);
-                                } while ($node->tagName !== $token['name']);
-                            }
-                            /*else {
-                                // XERROR: parse error
-                            }*/
-                        break;
-
-                        /* An end tag whose tag name is "dc", "dd", "ds", "dt" */
-                        case 'dc': case 'dd': case 'ds': case 'dt':
-                            if ($this->elementInScope($token['name'])) {
-                                $this->generateImpliedEndTags(array($token['name']));
-
-                                /* If the current node is not an element with the same
-                                tag name as the token, then this is a parse error. */
-                                // XERROR: implement parse error
-
-                                /* Pop elements from the stack of open elements  until
-                                 * an element with the same tag name as the token has
-                                 * been popped from the stack. */
-                                do {
-                                    $node = array_pop($this->stack);
-                                } while ($node->tagName !== $token['name']);
-                            }
-                            /*else {
-                                // XERROR: parse error
-                            }*/
-                        break;
-
-                        /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
-                        "h5", "h6" */
-                        case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
-                            $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
-
-                            /* If the stack of open elements has in scope an element whose
-                            tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
-                            generate implied end tags. */
-                            if ($this->elementInScope($elements)) {
-                                $this->generateImpliedEndTags();
-
-                                /* Now, if the current node is not an element with the same
-                                tag name as that of the token, then this is a parse error. */
-                                // XERROR: implement parse error
-
-                                /* If the stack of open elements has in scope an element
-                                whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
-                                "h6", then pop elements from the stack until an element
-                                with one of those tag names has been popped from the stack. */
-                                do {
-                                    $node = array_pop($this->stack);
-                                } while (!in_array($node->tagName, $elements));
-                            }
-                            /*else {
-                                // parse error
-                            }*/
-                        break;
-
-                        /* An end tag whose tag name is one of: "a", "b", "big", "em",
-                        "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
-                        case 'a': case 'b': case 'big': case 'code': case 'em': case 'font':
-                        case 'i': case 'nobr': case 's': case 'small': case 'strike':
-                        case 'strong': case 'tt': case 'u':
-                            // XERROR: generally speaking this needs parse error logic
-                            /* 1. Let the formatting element be the last element in
-                            the list of active formatting elements that:
-                                * is between the end of the list and the last scope
-                                marker in the list, if any, or the start of the list
-                                otherwise, and
-                                * has the same tag name as the token.
-                            */
-                            while (true) {
-                                for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
-                                    if ($this->a_formatting[$a] === self::MARKER) {
-                                        break;
-                                    } elseif ($this->a_formatting[$a]->tagName === $token['name']) {
-                                        $formatting_element = $this->a_formatting[$a];
-                                        $in_stack = in_array($formatting_element, $this->stack, true);
-                                        $fe_af_pos = $a;
-                                        break;
-                                    }
-                                }
-
-                                /* If there is no such node, or, if that node is
-                                also in the stack of open elements but the element
-                                is not in scope, then this is a parse error. Abort
-                                these steps. The token is ignored. */
-                                if (
-                                    !isset($formatting_element) || (
-                                        $in_stack &&
-                                        !$this->elementInScope($token['name'])
-                                    )
-                                ) {
-                                    $this->ignored = true;
-                                    break;
-
-                                /* Otherwise, if there is such a node, but that node
-                                is not in the stack of open elements, then this is a
-                                parse error; remove the element from the list, and
-                                abort these steps. */
-                                } elseif (isset($formatting_element) && !$in_stack) {
-                                    unset($this->a_formatting[$fe_af_pos]);
-                                    $this->a_formatting = array_merge($this->a_formatting);
-                                    break;
-                                }
-
-                                /* Otherwise, there is a formatting element and that
-                                 * element is in the stack and is in scope. If the
-                                 * element is not the current node, this is a parse
-                                 * error. In any case, proceed with the algorithm as
-                                 * written in the following steps. */
-                                // XERROR: implement me
-
-                                /* 2. Let the furthest block be the topmost node in the
-                                stack of open elements that is lower in the stack
-                                than the formatting element, and is not an element in
-                                the phrasing or formatting categories. There might
-                                not be one. */
-                                $fe_s_pos = array_search($formatting_element, $this->stack, true);
-                                $length = count($this->stack);
-
-                                for ($s = $fe_s_pos + 1; $s < $length; $s++) {
-                                    $category = $this->getElementCategory($this->stack[$s]);
-
-                                    if ($category !== self::PHRASING && $category !== self::FORMATTING) {
-                                        $furthest_block = $this->stack[$s];
-                                        break;
-                                    }
-                                }
-
-                                /* 3. If there is no furthest block, then the UA must
-                                skip the subsequent steps and instead just pop all
-                                the nodes from the bottom of the stack of open
-                                elements, from the current node up to the formatting
-                                element, and remove the formatting element from the
-                                list of active formatting elements. */
-                                if (!isset($furthest_block)) {
-                                    for ($n = $length - 1; $n >= $fe_s_pos; $n--) {
-                                        array_pop($this->stack);
-                                    }
-
-                                    unset($this->a_formatting[$fe_af_pos]);
-                                    $this->a_formatting = array_merge($this->a_formatting);
-                                    break;
-                                }
-
-                                /* 4. Let the common ancestor be the element
-                                immediately above the formatting element in the stack
-                                of open elements. */
-                                $common_ancestor = $this->stack[$fe_s_pos - 1];
-
-                                /* 5. Let a bookmark note the position of the
-                                formatting element in the list of active formatting
-                                elements relative to the elements on either side
-                                of it in the list. */
-                                $bookmark = $fe_af_pos;
-
-                                /* 6. Let node and last node  be the furthest block.
-                                Follow these steps: */
-                                $node = $furthest_block;
-                                $last_node = $furthest_block;
-
-                                while (true) {
-                                    for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
-                                        /* 6.1 Let node be the element immediately
-                                        prior to node in the stack of open elements. */
-                                        $node = $this->stack[$n];
-
-                                        /* 6.2 If node is not in the list of active
-                                        formatting elements, then remove node from
-                                        the stack of open elements and then go back
-                                        to step 1. */
-                                        if (!in_array($node, $this->a_formatting, true)) {
-                                            array_splice($this->stack, $n, 1);
-                                        } else {
-                                            break;
-                                        }
-                                    }
-
-                                    /* 6.3 Otherwise, if node is the formatting
-                                    element, then go to the next step in the overall
-                                    algorithm. */
-                                    if ($node === $formatting_element) {
-                                        break;
-
-                                    /* 6.4 Otherwise, if last node is the furthest
-                                    block, then move the aforementioned bookmark to
-                                    be immediately after the node in the list of
-                                    active formatting elements. */
-                                    } elseif ($last_node === $furthest_block) {
-                                        $bookmark = array_search($node, $this->a_formatting, true) + 1;
-                                    }
-
-                                    /* 6.5 Create an element for the token for which
-                                     * the element node was created, replace the entry
-                                     * for node in the list of active formatting
-                                     * elements with an entry for the new element,
-                                     * replace the entry for node in the stack of open
-                                     * elements with an entry for the new element, and
-                                     * let node be the new element. */
-                                    // we don't know what the token is anymore
-                                    // XDOM
-                                    $clone = $node->cloneNode();
-                                    $a_pos = array_search($node, $this->a_formatting, true);
-                                    $s_pos = array_search($node, $this->stack, true);
-                                    $this->a_formatting[$a_pos] = $clone;
-                                    $this->stack[$s_pos] = $clone;
-                                    $node = $clone;
-
-                                    /* 6.6 Insert last node into node, first removing
-                                    it from its previous parent node if any. */
-                                    // XDOM
-                                    if ($last_node->parentNode !== null) {
-                                        $last_node->parentNode->removeChild($last_node);
-                                    }
-
-                                    // XDOM
-                                    $node->appendChild($last_node);
-
-                                    /* 6.7 Let last node be node. */
-                                    $last_node = $node;
-
-                                    /* 6.8 Return to step 1 of this inner set of steps. */
-                                }
-
-                                /* 7. If the common ancestor node is a table, tbody,
-                                 * tfoot, thead, or tr element, then, foster parent
-                                 * whatever last node ended up being in the previous
-                                 * step, first removing it from its previous parent
-                                 * node if any. */
-                                // XDOM
-                                if ($last_node->parentNode) { // common step
-                                    $last_node->parentNode->removeChild($last_node);
-                                }
-                                if (in_array($common_ancestor->tagName, array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
-                                    $this->fosterParent($last_node);
-                                /* Otherwise, append whatever last node  ended up being
-                                 * in the previous step to the common ancestor node,
-                                 * first removing it from its previous parent node if
-                                 * any. */
-                                } else {
-                                    // XDOM
-                                    $common_ancestor->appendChild($last_node);
-                                }
-
-                                /* 8. Create an element for the token for which the
-                                 * formatting element was created. */
-                                // XDOM
-                                $clone = $formatting_element->cloneNode();
-
-                                /* 9. Take all of the child nodes of the furthest
-                                block and append them to the element created in the
-                                last step. */
-                                // XDOM
-                                while ($furthest_block->hasChildNodes()) {
-                                    $child = $furthest_block->firstChild;
-                                    $furthest_block->removeChild($child);
-                                    $clone->appendChild($child);
-                                }
-
-                                /* 10. Append that clone to the furthest block. */
-                                // XDOM
-                                $furthest_block->appendChild($clone);
-
-                                /* 11. Remove the formatting element from the list
-                                of active formatting elements, and insert the new element
-                                into the list of active formatting elements at the
-                                position of the aforementioned bookmark. */
-                                $fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
-                                array_splice($this->a_formatting, $fe_af_pos, 1);
-
-                                $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
-                                $af_part2 = array_slice($this->a_formatting, $bookmark);
-                                $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
-
-                                /* 12. Remove the formatting element from the stack
-                                of open elements, and insert the new element into the stack
-                                of open elements immediately below the position of the
-                                furthest block in that stack. */
-                                $fe_s_pos = array_search($formatting_element, $this->stack, true);
-                                array_splice($this->stack, $fe_s_pos, 1);
-
-                                $fb_s_pos = array_search($furthest_block, $this->stack, true);
-                                $s_part1 = array_slice($this->stack, 0, $fb_s_pos + 1);
-                                $s_part2 = array_slice($this->stack, $fb_s_pos + 1);
-                                $this->stack = array_merge($s_part1, array($clone), $s_part2);
-
-                                /* 13. Jump back to step 1 in this series of steps. */
-                                unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
-                            }
-                        break;
-
-                        case 'applet': case 'button': case 'marquee': case 'object':
-                            /* If the stack of open elements has an element in scope whose
-                            tag name matches the tag name of the token, then generate implied
-                            tags. */
-                            if ($this->elementInScope($token['name'])) {
-                                $this->generateImpliedEndTags();
-
-                                /* Now, if the current node is not an element with the same
-                                tag name as the token, then this is a parse error. */
-                                // XERROR: implement logic
-
-                                /* Pop elements from the stack of open elements  until
-                                 * an element with the same tag name as the token has
-                                 * been popped from the stack. */
-                                do {
-                                    $node = array_pop($this->stack);
-                                } while ($node->tagName !== $token['name']);
-
-                                /* Clear the list of active formatting elements up to the
-                                 * last marker. */
-                                $keys = array_keys($this->a_formatting, self::MARKER, true);
-                                $marker = end($keys);
-
-                                for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
-                                    array_pop($this->a_formatting);
-                                }
-                            }
-                            /*else {
-                                // parse error
-                            }*/
-                        break;
-
-                        case 'br':
-                            // Parse error
-                            $this->emitToken(array(
-                                'name' => 'br',
-                                'type' => HTML5_Tokenizer::STARTTAG,
-                            ));
-                        break;
-
-                        /* An end tag token not covered by the previous entries */
-                        default:
-                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                                /* Initialise node to be the current node (the bottommost
-                                node of the stack). */
-                                $node = $this->stack[$n];
-
-                                /* If node has the same tag name as the end tag token,
-                                then: */
-                                if ($token['name'] === $node->tagName) {
-                                    /* Generate implied end tags. */
-                                    $this->generateImpliedEndTags();
-
-                                    /* If the tag name of the end tag token does not
-                                    match the tag name of the current node, this is a
-                                    parse error. */
-                                    // XERROR: implement this
-
-                                    /* Pop all the nodes from the current node up to
-                                    node, including node, then stop these steps. */
-                                    // XSKETCHY
-                                    do {
-                                        $pop = array_pop($this->stack);
-                                    } while ($pop !== $node);
-                                    break;
-                                } else {
-                                    $category = $this->getElementCategory($node);
-
-                                    if ($category !== self::FORMATTING && $category !== self::PHRASING) {
-                                        /* Otherwise, if node is in neither the formatting
-                                        category nor the phrasing category, then this is a
-                                        parse error. Stop this algorithm. The end tag token
-                                        is ignored. */
-                                        $this->ignored = true;
-                                        break;
-                                        // parse error
-                                    }
-                                }
-                                /* Set node to the previous entry in the stack of open elements. Loop. */
-                            }
-                        break;
-                    }
-                    break;
-                }
-                break;
-
-            case self::IN_CDATA_RCDATA:
-                if (
-                    $token['type'] === HTML5_Tokenizer::CHARACTER ||
-                    $token['type'] === HTML5_Tokenizer::SPACECHARACTER
-                ) {
-                    $this->insertText($token['data']);
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    // parse error
-                    /* If the current node is a script  element, mark the script
-                     * element as "already executed". */
-                    // probably not necessary
-                    array_pop($this->stack);
-                    $this->mode = $this->original_mode;
-                    $this->emitToken($token);
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'script') {
-                    array_pop($this->stack);
-                    $this->mode = $this->original_mode;
-                    // we're ignoring all of the execution stuff
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG) {
-                    array_pop($this->stack);
-                    $this->mode = $this->original_mode;
-                }
-            break;
-
-            case self::IN_TABLE:
-                $clear = array('html', 'table');
-
-                /* A character token */
-                if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
-                    $token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
-                    /* Let the pending table character tokens
-                     * be an empty list of tokens. */
-                    $this->pendingTableCharacters = "";
-                    $this->pendingTableCharactersDirty = false;
-                    /* Let the original insertion mode be the current
-                     * insertion mode. */
-                    $this->original_mode = $this->mode;
-                    /* Switch the insertion mode to
-                     * "in table text" and
-                     * reprocess the token. */
-                    $this->mode = self::IN_TABLE_TEXT;
-                    $this->emitToken($token);
-
-                /* A comment token */
-                } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the current node with the data
-                    attribute set to the data given in the comment token. */
-                    $this->insertComment($token['data']);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
-                    // parse error
-
-                /* A start tag whose tag name is "caption" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'caption') {
-                    /* Clear the stack back to a table context. */
-                    $this->clearStackToTableContext($clear);
-
-                    /* Insert a marker at the end of the list of active
-                    formatting elements. */
-                    $this->a_formatting[] = self::MARKER;
-
-                    /* Insert an HTML element for the token, then switch the
-                    insertion mode to "in caption". */
-                    $this->insertElement($token);
-                    $this->mode = self::IN_CAPTION;
-
-                /* A start tag whose tag name is "colgroup" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'colgroup') {
-                    /* Clear the stack back to a table context. */
-                    $this->clearStackToTableContext($clear);
-
-                    /* Insert an HTML element for the token, then switch the
-                    insertion mode to "in column group". */
-                    $this->insertElement($token);
-                    $this->mode = self::IN_COLUMN_GROUP;
-
-                /* A start tag whose tag name is "col" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'col') {
-                    $this->emitToken(array(
-                        'name' => 'colgroup',
-                        'type' => HTML5_Tokenizer::STARTTAG,
-                        'attr' => array()
-                    ));
-
-                    $this->emitToken($token);
-
-                /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
-                array('tbody', 'tfoot', 'thead'))) {
-                    /* Clear the stack back to a table context. */
-                    $this->clearStackToTableContext($clear);
-
-                    /* Insert an HTML element for the token, then switch the insertion
-                    mode to "in table body". */
-                    $this->insertElement($token);
-                    $this->mode = self::IN_TABLE_BODY;
-
-                /* A start tag whose tag name is one of: "td", "th", "tr" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                in_array($token['name'], array('td', 'th', 'tr'))) {
-                    /* Act as if a start tag token with the tag name "tbody" had been
-                    seen, then reprocess the current token. */
-                    $this->emitToken(array(
-                        'name' => 'tbody',
-                        'type' => HTML5_Tokenizer::STARTTAG,
-                        'attr' => array()
-                    ));
-
-                    $this->emitToken($token);
-
-                /* A start tag whose tag name is "table" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'table') {
-                    /* Parse error. Act as if an end tag token with the tag name "table"
-                    had been seen, then, if that token wasn't ignored, reprocess the
-                    current token. */
-                    $this->emitToken(array(
-                        'name' => 'table',
-                        'type' => HTML5_Tokenizer::ENDTAG
-                    ));
-
-                    if (!$this->ignored) {
-                        $this->emitToken($token);
-                    }
-
-                /* An end tag whose tag name is "table" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'table') {
-                    /* If the stack of open elements does not have an element in table
-                    scope with the same tag name as the token, this is a parse error.
-                    Ignore the token. (fragment case) */
-                    if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        $this->ignored = true;
-                    } else {
-                        do {
-                            $node = array_pop($this->stack);
-                        } while ($node->tagName !== 'table');
-
-                        /* Reset the insertion mode appropriately. */
-                        $this->resetInsertionMode();
-                    }
-
-                /* An end tag whose tag name is one of: "body", "caption", "col",
-                "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
-                array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td',
-                'tfoot', 'th', 'thead', 'tr'))) {
-                    // Parse error. Ignore the token.
-
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                ($token['name'] === 'style' || $token['name'] === 'script')) {
-                    $this->processWithRulesFor($token, self::IN_HEAD);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'input' &&
-                // assignment is intentional
-                /* If the token does not have an attribute with the name "type", or
-                 * if it does, but that attribute's value is not an ASCII
-                 * case-insensitive match for the string "hidden", then: act as
-                 * described in the "anything else" entry below. */
-                ($type = $this->getAttr($token, 'type')) && strtolower($type) === 'hidden') {
-                    // I.e., if its an input with the type attribute == 'hidden'
-                    /* Otherwise */
-                    // parse error
-                    $this->insertElement($token);
-                    array_pop($this->stack);
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    /* If the current node is not the root html element, then this is a parse error. */
-                    if (end($this->stack)->tagName !== 'html') {
-                        // Note: It can only be the current node in the fragment case.
-                        // parse error
-                    }
-                    /* Stop parsing. */
-                /* Anything else */
-                } else {
-                    /* Parse error. Process the token as if the insertion mode was "in
-                    body", with the following exception: */
-
-                    $old = $this->foster_parent;
-                    $this->foster_parent = true;
-                    $this->processWithRulesFor($token, self::IN_BODY);
-                    $this->foster_parent = $old;
-                }
-            break;
-
-            case self::IN_TABLE_TEXT:
-                /* A character token */
-                if ($token['type'] === HTML5_Tokenizer::CHARACTER) {
-                    /* Append the character token to the pending table
-                     * character tokens list. */
-                    $this->pendingTableCharacters .= $token['data'];
-                    $this->pendingTableCharactersDirty = true;
-                } elseif ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
-                    $this->pendingTableCharacters .= $token['data'];
-                /* Anything else */
-                } else {
-                    if ($this->pendingTableCharacters !== '' && is_string($this->pendingTableCharacters)) {
-                        /* If any of the tokens in the pending table character tokens list
-                         * are character tokens that are not one of U+0009 CHARACTER
-                         * TABULATION, U+000A LINE FEED (LF), U+000C FORM FEED (FF), or
-                         * U+0020 SPACE, then reprocess those character tokens using the
-                         * rules given in the "anything else" entry in the in table"
-                         * insertion mode.*/
-                        if ($this->pendingTableCharactersDirty) {
-                            /* Parse error. Process the token using the rules for the
-                             * "in body" insertion mode, except that if the current
-                             * node is a table, tbody, tfoot, thead, or tr element,
-                             * then, whenever a node would be inserted into the current
-                             * node, it must instead be foster parented. */
-                            // XERROR
-                            $old = $this->foster_parent;
-                            $this->foster_parent = true;
-                            $text_token = array(
-                                'type' => HTML5_Tokenizer::CHARACTER,
-                                'data' => $this->pendingTableCharacters,
-                            );
-                            $this->processWithRulesFor($text_token, self::IN_BODY);
-                            $this->foster_parent = $old;
-
-                        /* Otherwise, insert the characters given by the pending table
-                         * character tokens list into the current node. */
-                        } else {
-                            $this->insertText($this->pendingTableCharacters);
-                        }
-                        $this->pendingTableCharacters = null;
-                        $this->pendingTableCharactersNull = null;
-                    }
-
-                    /* Switch the insertion mode to the original insertion mode and
-                     * reprocess the token.
-                     */
-                    $this->mode = $this->original_mode;
-                    $this->emitToken($token);
-                }
-            break;
-
-            case self::IN_CAPTION:
-                /* An end tag whose tag name is "caption" */
-                if ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'caption') {
-                    /* If the stack of open elements does not have an element in table
-                    scope with the same tag name as the token, this is a parse error.
-                    Ignore the token. (fragment case) */
-                    if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        $this->ignored = true;
-                        // Ignore
-
-                    /* Otherwise: */
-                    } else {
-                        /* Generate implied end tags. */
-                        $this->generateImpliedEndTags();
-
-                        /* Now, if the current node is not a caption element, then this
-                        is a parse error. */
-                        // XERROR: implement
-
-                        /* Pop elements from this stack until a caption element has
-                        been popped from the stack. */
-                        do {
-                            $node = array_pop($this->stack);
-                        } while ($node->tagName !== 'caption');
-
-                        /* Clear the list of active formatting elements up to the last
-                        marker. */
-                        $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
-                        /* Switch the insertion mode to "in table". */
-                        $this->mode = self::IN_TABLE;
-                    }
-
-                /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-                "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
-                name is "table" */
-                } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
-                array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
-                'thead', 'tr'))) || ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'table')) {
-                    /* Parse error. Act as if an end tag with the tag name "caption"
-                    had been seen, then, if that token wasn't ignored, reprocess the
-                    current token. */
-                    $this->emitToken(array(
-                        'name' => 'caption',
-                        'type' => HTML5_Tokenizer::ENDTAG
-                    ));
-
-                    if (!$this->ignored) {
-                        $this->emitToken($token);
-                    }
-
-                /* An end tag whose tag name is one of: "body", "col", "colgroup",
-                "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
-                array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th',
-                'thead', 'tr'))) {
-                    // Parse error. Ignore the token.
-                    $this->ignored = true;
-                } else {
-                    /* Process the token as if the insertion mode was "in body". */
-                    $this->processWithRulesFor($token, self::IN_BODY);
-                }
-            break;
-
-            case self::IN_COLUMN_GROUP:
-                /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-                U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-                or U+0020 SPACE */
-                if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
-                    /* Append the character to the current node. */
-                    $this->insertText($token['data']);
-
-                /* A comment token */
-                } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the current node with the data
-                    attribute set to the data given in the comment token. */
-                    $this->insertComment($token['data']);
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
-                    // parse error
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
-                    $this->processWithRulesFor($token, self::IN_BODY);
-
-                /* A start tag whose tag name is "col" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'col') {
-                    /* Insert a col element for the token. Immediately pop the current
-                    node off the stack of open elements. */
-                    $this->insertElement($token);
-                    array_pop($this->stack);
-                    // XERROR: Acknowledge the token's self-closing flag, if it is set.
-
-                /* An end tag whose tag name is "colgroup" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'colgroup') {
-                    /* If the current node is the root html element, then this is a
-                    parse error, ignore the token. (fragment case) */
-                    if (end($this->stack)->tagName === 'html') {
-                        $this->ignored = true;
-
-                    /* Otherwise, pop the current node (which will be a colgroup
-                    element) from the stack of open elements. Switch the insertion
-                    mode to "in table". */
-                    } else {
-                        array_pop($this->stack);
-                        $this->mode = self::IN_TABLE;
-                    }
-
-                /* An end tag whose tag name is "col" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'col') {
-                    /* Parse error. Ignore the token. */
-                    $this->ignored = true;
-
-                /* An end-of-file token */
-                /* If the current node is the root html  element */
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF && end($this->stack)->tagName === 'html') {
-                    /* Stop parsing */
-
-                /* Anything else */
-                } else {
-                    /* Act as if an end tag with the tag name "colgroup" had been seen,
-                    and then, if that token wasn't ignored, reprocess the current token. */
-                    $this->emitToken(array(
-                        'name' => 'colgroup',
-                        'type' => HTML5_Tokenizer::ENDTAG
-                    ));
-
-                    if (!$this->ignored) {
-                        $this->emitToken($token);
-                    }
-                }
-            break;
-
-            case self::IN_TABLE_BODY:
-                $clear = array('tbody', 'tfoot', 'thead', 'html');
-
-                /* A start tag whose tag name is "tr" */
-                if ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'tr') {
-                    /* Clear the stack back to a table body context. */
-                    $this->clearStackToTableContext($clear);
-
-                    /* Insert a tr element for the token, then switch the insertion
-                    mode to "in row". */
-                    $this->insertElement($token);
-                    $this->mode = self::IN_ROW;
-
-                /* A start tag whose tag name is one of: "th", "td" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                ($token['name'] === 'th' ||    $token['name'] === 'td')) {
-                    /* Parse error. Act as if a start tag with the tag name "tr" had
-                    been seen, then reprocess the current token. */
-                    $this->emitToken(array(
-                        'name' => 'tr',
-                        'type' => HTML5_Tokenizer::STARTTAG,
-                        'attr' => array()
-                    ));
-
-                    $this->emitToken($token);
-
-                /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
-                    /* If the stack of open elements does not have an element in table
-                    scope with the same tag name as the token, this is a parse error.
-                    Ignore the token. */
-                    if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        // Parse error
-                        $this->ignored = true;
-
-                    /* Otherwise: */
-                    } else {
-                        /* Clear the stack back to a table body context. */
-                        $this->clearStackToTableContext($clear);
-
-                        /* Pop the current node from the stack of open elements. Switch
-                        the insertion mode to "in table". */
-                        array_pop($this->stack);
-                        $this->mode = self::IN_TABLE;
-                    }
-
-                /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-                "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
-                } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
-                array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead'))) ||
-                ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
-                    /* If the stack of open elements does not have a tbody, thead, or
-                    tfoot element in table scope, this is a parse error. Ignore the
-                    token. (fragment case) */
-                    if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), self::SCOPE_TABLE)) {
-                        // parse error
-                        $this->ignored = true;
-
-                    /* Otherwise: */
-                    } else {
-                        /* Clear the stack back to a table body context. */
-                        $this->clearStackToTableContext($clear);
-
-                        /* Act as if an end tag with the same tag name as the current
-                        node ("tbody", "tfoot", or "thead") had been seen, then
-                        reprocess the current token. */
-                        $this->emitToken(array(
-                            'name' => end($this->stack)->tagName,
-                            'type' => HTML5_Tokenizer::ENDTAG
-                        ));
-
-                        $this->emitToken($token);
-                    }
-
-                /* An end tag whose tag name is one of: "body", "caption", "col",
-                "colgroup", "html", "td", "th", "tr" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
-                array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) {
-                    /* Parse error. Ignore the token. */
-                    $this->ignored = true;
-
-                /* Anything else */
-                } else {
-                    /* Process the token as if the insertion mode was "in table". */
-                    $this->processWithRulesFor($token, self::IN_TABLE);
-                }
-            break;
-
-            case self::IN_ROW:
-                $clear = array('tr', 'html');
-
-                /* A start tag whose tag name is one of: "th", "td" */
-                if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                ($token['name'] === 'th' || $token['name'] === 'td')) {
-                    /* Clear the stack back to a table row context. */
-                    $this->clearStackToTableContext($clear);
-
-                    /* Insert an HTML element for the token, then switch the insertion
-                    mode to "in cell". */
-                    $this->insertElement($token);
-                    $this->mode = self::IN_CELL;
-
-                    /* Insert a marker at the end of the list of active formatting
-                    elements. */
-                    $this->a_formatting[] = self::MARKER;
-
-                /* An end tag whose tag name is "tr" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'tr') {
-                    /* If the stack of open elements does not have an element in table
-                    scope with the same tag name as the token, this is a parse error.
-                    Ignore the token. (fragment case) */
-                    if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        // Ignore.
-                        $this->ignored = true;
-                    } else {
-                        /* Clear the stack back to a table row context. */
-                        $this->clearStackToTableContext($clear);
-
-                        /* Pop the current node (which will be a tr element) from the
-                        stack of open elements. Switch the insertion mode to "in table
-                        body". */
-                        array_pop($this->stack);
-                        $this->mode = self::IN_TABLE_BODY;
-                    }
-
-                /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-                "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
-                } elseif (($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
-                array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) ||
-                ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'table')) {
-                    /* Act as if an end tag with the tag name "tr" had been seen, then,
-                    if that token wasn't ignored, reprocess the current token. */
-                    $this->emitToken(array(
-                        'name' => 'tr',
-                        'type' => HTML5_Tokenizer::ENDTAG
-                    ));
-                    if (!$this->ignored) {
-                        $this->emitToken($token);
-                    }
-
-                /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                in_array($token['name'], array('tbody', 'tfoot', 'thead'))) {
-                    /* If the stack of open elements does not have an element in table
-                    scope with the same tag name as the token, this is a parse error.
-                    Ignore the token. */
-                    if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        $this->ignored = true;
-
-                    /* Otherwise: */
-                    } else {
-                        /* Otherwise, act as if an end tag with the tag name "tr" had
-                        been seen, then reprocess the current token. */
-                        $this->emitToken(array(
-                            'name' => 'tr',
-                            'type' => HTML5_Tokenizer::ENDTAG
-                        ));
-
-                        $this->emitToken($token);
-                    }
-
-                /* An end tag whose tag name is one of: "body", "caption", "col",
-                "colgroup", "html", "td", "th" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
-                array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th'))) {
-                    /* Parse error. Ignore the token. */
-                    $this->ignored = true;
-
-                /* Anything else */
-                } else {
-                    /* Process the token as if the insertion mode was "in table". */
-                    $this->processWithRulesFor($token, self::IN_TABLE);
-                }
-            break;
-
-            case self::IN_CELL:
-                /* An end tag whose tag name is one of: "td", "th" */
-                if ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                ($token['name'] === 'td' || $token['name'] === 'th')) {
-                    /* If the stack of open elements does not have an element in table
-                    scope with the same tag name as that of the token, then this is a
-                    parse error and the token must be ignored. */
-                    if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        $this->ignored = true;
-
-                    /* Otherwise: */
-                    } else {
-                        /* Generate implied end tags, except for elements with the same
-                        tag name as the token. */
-                        $this->generateImpliedEndTags(array($token['name']));
-
-                        /* Now, if the current node is not an element with the same tag
-                        name as the token, then this is a parse error. */
-                        // XERROR: Implement parse error code
-
-                        /* Pop elements from this stack until an element with the same
-                        tag name as the token has been popped from the stack. */
-                        do {
-                            $node = array_pop($this->stack);
-                        } while ($node->tagName !== $token['name']);
-
-                        /* Clear the list of active formatting elements up to the last
-                        marker. */
-                        $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
-                        /* Switch the insertion mode to "in row". (The current node
-                        will be a tr element at this point.) */
-                        $this->mode = self::IN_ROW;
-                    }
-
-                /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-                "tbody", "td", "tfoot", "th", "thead", "tr" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && in_array($token['name'],
-                array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
-                'thead', 'tr'))) {
-                    /* If the stack of open elements does not have a td or th element
-                    in table scope, then this is a parse error; ignore the token.
-                    (fragment case) */
-                    if (!$this->elementInScope(array('td', 'th'), self::SCOPE_TABLE)) {
-                        // parse error
-                        $this->ignored = true;
-
-                    /* Otherwise, close the cell (see below) and reprocess the current
-                    token. */
-                    } else {
-                        $this->closeCell();
-                        $this->emitToken($token);
-                    }
-
-                /* An end tag whose tag name is one of: "body", "caption", "col",
-                "colgroup", "html" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
-                array('body', 'caption', 'col', 'colgroup', 'html'))) {
-                    /* Parse error. Ignore the token. */
-                    $this->ignored = true;
-
-                /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
-                "thead", "tr" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && in_array($token['name'],
-                array('table', 'tbody', 'tfoot', 'thead', 'tr'))) {
-                    /* If the stack of open elements does not have a td or th element
-                    in table scope, then this is a parse error; ignore the token.
-                    (innerHTML case) */
-                    if (!$this->elementInScope(array('td', 'th'), self::SCOPE_TABLE)) {
-                        // Parse error
-                        $this->ignored = true;
-
-                    /* Otherwise, close the cell (see below) and reprocess the current
-                    token. */
-                    } else {
-                        $this->closeCell();
-                        $this->emitToken($token);
-                    }
-
-                /* Anything else */
-                } else {
-                    /* Process the token as if the insertion mode was "in body". */
-                    $this->processWithRulesFor($token, self::IN_BODY);
-                }
-            break;
-
-            case self::IN_SELECT:
-                /* Handle the token as follows: */
-
-                /* A character token */
-                if (
-                    $token['type'] === HTML5_Tokenizer::CHARACTER ||
-                    $token['type'] === HTML5_Tokenizer::SPACECHARACTER
-                ) {
-                    /* Append the token's character to the current node. */
-                    $this->insertText($token['data']);
-
-                /* A comment token */
-                } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the current node with the data
-                    attribute set to the data given in the comment token. */
-                    $this->insertComment($token['data']);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
-                    // parse error
-
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
-                    $this->processWithRulesFor($token, self::IN_BODY);
-
-                /* A start tag token whose tag name is "option" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'option') {
-                    /* If the current node is an option element, act as if an end tag
-                    with the tag name "option" had been seen. */
-                    if (end($this->stack)->tagName === 'option') {
-                        $this->emitToken(array(
-                            'name' => 'option',
-                            'type' => HTML5_Tokenizer::ENDTAG
-                        ));
-                    }
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                /* A start tag token whose tag name is "optgroup" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'optgroup') {
-                    /* If the current node is an option element, act as if an end tag
-                    with the tag name "option" had been seen. */
-                    if (end($this->stack)->tagName === 'option') {
-                        $this->emitToken(array(
-                            'name' => 'option',
-                            'type' => HTML5_Tokenizer::ENDTAG
-                        ));
-                    }
-
-                    /* If the current node is an optgroup element, act as if an end tag
-                    with the tag name "optgroup" had been seen. */
-                    if (end($this->stack)->tagName === 'optgroup') {
-                        $this->emitToken(array(
-                            'name' => 'optgroup',
-                            'type' => HTML5_Tokenizer::ENDTAG
-                        ));
-                    }
-
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                /* An end tag token whose tag name is "optgroup" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'optgroup') {
-                    /* First, if the current node is an option element, and the node
-                    immediately before it in the stack of open elements is an optgroup
-                    element, then act as if an end tag with the tag name "option" had
-                    been seen. */
-                    $elements_in_stack = count($this->stack);
-
-                    if ($this->stack[$elements_in_stack - 1]->tagName === 'option' &&
-                    $this->stack[$elements_in_stack - 2]->tagName === 'optgroup') {
-                        $this->emitToken(array(
-                            'name' => 'option',
-                            'type' => HTML5_Tokenizer::ENDTAG
-                        ));
-                    }
-
-                    /* If the current node is an optgroup element, then pop that node
-                    from the stack of open elements. Otherwise, this is a parse error,
-                    ignore the token. */
-                    if (end($this->stack)->tagName === 'optgroup') {
-                        array_pop($this->stack);
-                    } else {
-                        // parse error
-                        $this->ignored = true;
-                    }
-
-                /* An end tag token whose tag name is "option" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'option') {
-                    /* If the current node is an option element, then pop that node
-                    from the stack of open elements. Otherwise, this is a parse error,
-                    ignore the token. */
-                    if (end($this->stack)->tagName === 'option') {
-                        array_pop($this->stack);
-                    } else {
-                        // parse error
-                        $this->ignored = true;
-                    }
-
-                /* An end tag whose tag name is "select" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'select') {
-                    /* If the stack of open elements does not have an element in table
-                    scope with the same tag name as the token, this is a parse error.
-                    Ignore the token. (fragment case) */
-                    if (!$this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        $this->ignored = true;
-                        // parse error
-
-                    /* Otherwise: */
-                    } else {
-                        /* Pop elements from the stack of open elements until a select
-                        element has been popped from the stack. */
-                        do {
-                            $node = array_pop($this->stack);
-                        } while ($node->tagName !== 'select');
-
-                        /* Reset the insertion mode appropriately. */
-                        $this->resetInsertionMode();
-                    }
-
-                /* A start tag whose tag name is "select" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'select') {
-                    /* Parse error. Act as if the token had been an end tag with the
-                    tag name "select" instead. */
-                    $this->emitToken(array(
-                        'name' => 'select',
-                        'type' => HTML5_Tokenizer::ENDTAG
-                    ));
-
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                ($token['name'] === 'input' || $token['name'] === 'keygen' ||  $token['name'] === 'textarea')) {
-                    // parse error
-                    $this->emitToken(array(
-                        'name' => 'select',
-                        'type' => HTML5_Tokenizer::ENDTAG
-                    ));
-                    $this->emitToken($token);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'script') {
-                    $this->processWithRulesFor($token, self::IN_HEAD);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    // XERROR: If the current node is not the root html element, then this is a parse error.
-                    /* Stop parsing */
-
-                /* Anything else */
-                } else {
-                    /* Parse error. Ignore the token. */
-                    $this->ignored = true;
-                }
-            break;
-
-            case self::IN_SELECT_IN_TABLE:
-
-                if ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                in_array($token['name'], array('caption', 'table', 'tbody',
-                'tfoot', 'thead', 'tr', 'td', 'th'))) {
-                    // parse error
-                    $this->emitToken(array(
-                        'name' => 'select',
-                        'type' => HTML5_Tokenizer::ENDTAG,
-                    ));
-                    $this->emitToken($token);
-
-                /* An end tag whose tag name is one of: "caption", "table", "tbody",
-                "tfoot", "thead", "tr", "td", "th" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                in_array($token['name'], array('caption', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'td', 'th')))  {
-                    /* Parse error. */
-                    // parse error
-
-                    /* If the stack of open elements has an element in table scope with
-                    the same tag name as that of the token, then act as if an end tag
-                    with the tag name "select" had been seen, and reprocess the token.
-                    Otherwise, ignore the token. */
-                    if ($this->elementInScope($token['name'], self::SCOPE_TABLE)) {
-                        $this->emitToken(array(
-                            'name' => 'select',
-                            'type' => HTML5_Tokenizer::ENDTAG
-                        ));
-
-                        $this->emitToken($token);
-                    } else {
-                        $this->ignored = true;
-                    }
-                } else {
-                    $this->processWithRulesFor($token, self::IN_SELECT);
-                }
-            break;
-
-            case self::IN_FOREIGN_CONTENT:
-                if ($token['type'] === HTML5_Tokenizer::CHARACTER ||
-                $token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
-                    $this->insertText($token['data']);
-                } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    $this->insertComment($token['data']);
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
-                    // XERROR: parse error
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'script' && end($this->stack)->tagName === 'script' &&
-                // XDOM
-                end($this->stack)->namespaceURI === self::NS_SVG) {
-                    array_pop($this->stack);
-                    // a bunch of script running mumbo jumbo
-                } elseif (
-                    ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                        ((
-                            $token['name'] !== 'mglyph' &&
-                            $token['name'] !== 'malignmark' &&
-                            // XDOM
-                            end($this->stack)->namespaceURI === self::NS_MATHML &&
-                            in_array(end($this->stack)->tagName, array('mi', 'mo', 'mn', 'ms', 'mtext'))
-                        ) ||
-                        (
-                            $token['name'] === 'svg' &&
-                            // XDOM
-                            end($this->stack)->namespaceURI === self::NS_MATHML &&
-                            end($this->stack)->tagName === 'annotation-xml'
-                        ) ||
-                        (
-                            // XDOM
-                            end($this->stack)->namespaceURI === self::NS_SVG &&
-                            in_array(end($this->stack)->tagName, array('foreignObject', 'desc', 'title'))
-                        ) ||
-                        (
-                            // XSKETCHY && XDOM
-                            end($this->stack)->namespaceURI === self::NS_HTML
-                        ))
-                    ) || $token['type'] === HTML5_Tokenizer::ENDTAG
-                ) {
-                    $this->processWithRulesFor($token, $this->secondary_mode);
-                    /* If, after doing so, the insertion mode is still "in foreign
-                     * content", but there is no element in scope that has a namespace
-                     * other than the HTML namespace, switch the insertion mode to the
-                     * secondary insertion mode. */
-                    if ($this->mode === self::IN_FOREIGN_CONTENT) {
-                        $found = false;
-                        // this basically duplicates elementInScope()
-                        for ($i = count($this->stack) - 1; $i >= 0; $i--) {
-                            // XDOM
-                            $node = $this->stack[$i];
-                            if ($node->namespaceURI !== self::NS_HTML) {
-                                $found = true;
-                                break;
-                            } elseif (in_array($node->tagName, array('table', 'html',
-                            'applet', 'caption', 'td', 'th', 'button', 'marquee',
-                            'object')) || ($node->tagName === 'foreignObject' &&
-                            $node->namespaceURI === self::NS_SVG)) {
-                                break;
-                            }
-                        }
-                        if (!$found) {
-                            $this->mode = $this->secondary_mode;
-                        }
-                    }
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF || (
-                $token['type'] === HTML5_Tokenizer::STARTTAG &&
-                (in_array($token['name'], array('b', "big", "blockquote", "body", "br",
-                "center", "code", "dc", "dd", "div", "dl", "ds", "dt", "em", "embed", "h1", "h2",
-                "h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing",
-                "menu", "meta", "nobr", "ol", "p", "pre", "ruby", "s",  "small",
-                "span", "strong", "strike",  "sub", "sup", "table", "tt", "u", "ul",
-                "var")) || ($token['name'] === 'font' && ($this->getAttr($token, 'color') ||
-                $this->getAttr($token, 'face') || $this->getAttr($token, 'size')))))) {
-                    // XERROR: parse error
-                    do {
-                        $node = array_pop($this->stack);
-                        // XDOM
-                    } while ($node->namespaceURI !== self::NS_HTML);
-                    $this->stack[] = $node;
-                    $this->mode = $this->secondary_mode;
-                    $this->emitToken($token);
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG) {
-                    static $svg_lookup = array(
-                        'altglyph' => 'altGlyph',
-                        'altglyphdef' => 'altGlyphDef',
-                        'altglyphitem' => 'altGlyphItem',
-                        'animatecolor' => 'animateColor',
-                        'animatemotion' => 'animateMotion',
-                        'animatetransform' => 'animateTransform',
-                        'clippath' => 'clipPath',
-                        'feblend' => 'feBlend',
-                        'fecolormatrix' => 'feColorMatrix',
-                        'fecomponenttransfer' => 'feComponentTransfer',
-                        'fecomposite' => 'feComposite',
-                        'feconvolvematrix' => 'feConvolveMatrix',
-                        'fediffuselighting' => 'feDiffuseLighting',
-                        'fedisplacementmap' => 'feDisplacementMap',
-                        'fedistantlight' => 'feDistantLight',
-                        'feflood' => 'feFlood',
-                        'fefunca' => 'feFuncA',
-                        'fefuncb' => 'feFuncB',
-                        'fefuncg' => 'feFuncG',
-                        'fefuncr' => 'feFuncR',
-                        'fegaussianblur' => 'feGaussianBlur',
-                        'feimage' => 'feImage',
-                        'femerge' => 'feMerge',
-                        'femergenode' => 'feMergeNode',
-                        'femorphology' => 'feMorphology',
-                        'feoffset' => 'feOffset',
-                        'fepointlight' => 'fePointLight',
-                        'fespecularlighting' => 'feSpecularLighting',
-                        'fespotlight' => 'feSpotLight',
-                        'fetile' => 'feTile',
-                        'feturbulence' => 'feTurbulence',
-                        'foreignobject' => 'foreignObject',
-                        'glyphref' => 'glyphRef',
-                        'lineargradient' => 'linearGradient',
-                        'radialgradient' => 'radialGradient',
-                        'textpath' => 'textPath',
-                    );
-                    // XDOM
-                    $current = end($this->stack);
-                    if ($current->namespaceURI === self::NS_MATHML) {
-                        $token = $this->adjustMathMLAttributes($token);
-                    }
-                    if ($current->namespaceURI === self::NS_SVG &&
-                    isset($svg_lookup[$token['name']])) {
-                        $token['name'] = $svg_lookup[$token['name']];
-                    }
-                    if ($current->namespaceURI === self::NS_SVG) {
-                        $token = $this->adjustSVGAttributes($token);
-                    }
-                    $token = $this->adjustForeignAttributes($token);
-                    $this->insertForeignElement($token, $current->namespaceURI);
-                    if (isset($token['self-closing'])) {
-                        array_pop($this->stack);
-                        // XERROR: acknowledge self-closing flag
-                    }
-                }
-            break;
-
-            case self::AFTER_BODY:
-                /* Handle the token as follows: */
-
-                /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-                U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-                or U+0020 SPACE */
-                if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
-                    /* Process the token as it would be processed if the insertion mode
-                    was "in body". */
-                    $this->processWithRulesFor($token, self::IN_BODY);
-
-                /* A comment token */
-                } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the first element in the stack of open
-                    elements (the html element), with the data attribute set to the
-                    data given in the comment token. */
-                    // XDOM
-                    $comment = $this->dom->createComment($token['data']);
-                    $this->stack[0]->appendChild($comment);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
-                    // parse error
-
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
-                    $this->processWithRulesFor($token, self::IN_BODY);
-
-                /* An end tag with the tag name "html" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG && $token['name'] === 'html') {
-                    /*     If the parser was originally created as part of the HTML
-                     *     fragment parsing algorithm, this is a parse error; ignore
-                     *     the token. (fragment case) */
-                    $this->ignored = true;
-                    // XERROR: implement this
-
-                    $this->mode = self::AFTER_AFTER_BODY;
-
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    /* Stop parsing */
-
-                /* Anything else */
-                } else {
-                    /* Parse error. Set the insertion mode to "in body" and reprocess
-                    the token. */
-                    $this->mode = self::IN_BODY;
-                    $this->emitToken($token);
-                }
-            break;
-
-            case self::IN_FRAMESET:
-                /* Handle the token as follows: */
-
-                /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-                U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-                U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
-                if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
-                    /* Append the character to the current node. */
-                    $this->insertText($token['data']);
-
-                /* A comment token */
-                } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the current node with the data
-                    attribute set to the data given in the comment token. */
-                    $this->insertComment($token['data']);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
-                    // parse error
-
-                /* A start tag with the tag name "frameset" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'frameset') {
-                    $this->insertElement($token);
-
-                /* An end tag with the tag name "frameset" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'frameset') {
-                    /* If the current node is the root html element, then this is a
-                    parse error; ignore the token. (fragment case) */
-                    if (end($this->stack)->tagName === 'html') {
-                        $this->ignored = true;
-                        // Parse error
-
-                    } else {
-                        /* Otherwise, pop the current node from the stack of open
-                        elements. */
-                        array_pop($this->stack);
-
-                        /* If the parser was not originally created as part of the HTML
-                         * fragment parsing algorithm  (fragment case), and the current
-                         * node is no longer a frameset element, then switch the
-                         * insertion mode to "after frameset". */
-                        $this->mode = self::AFTER_FRAMESET;
-                    }
-
-                /* A start tag with the tag name "frame" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'frame') {
-                    /* Insert an HTML element for the token. */
-                    $this->insertElement($token);
-
-                    /* Immediately pop the current node off the stack of open elements. */
-                    array_pop($this->stack);
-
-                    // XERROR: Acknowledge the token's self-closing flag, if it is set.
-
-                /* A start tag with the tag name "noframes" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'noframes') {
-                    /* Process the token using the rules for the "in head" insertion mode. */
-                    $this->processwithRulesFor($token, self::IN_HEAD);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    // XERROR: If the current node is not the root html element, then this is a parse error.
-                    /* Stop parsing */
-                /* Anything else */
-                } else {
-                    /* Parse error. Ignore the token. */
-                    $this->ignored = true;
-                }
-            break;
-
-            case self::AFTER_FRAMESET:
-                /* Handle the token as follows: */
-
-                /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-                U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-                U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
-                if ($token['type'] === HTML5_Tokenizer::SPACECHARACTER) {
-                    /* Append the character to the current node. */
-                    $this->insertText($token['data']);
-
-                /* A comment token */
-                } elseif ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the current node with the data
-                    attribute set to the data given in the comment token. */
-                    $this->insertComment($token['data']);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE) {
-                    // parse error
-
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html') {
-                    $this->processWithRulesFor($token, self::IN_BODY);
-
-                /* An end tag with the tag name "html" */
-                } elseif ($token['type'] === HTML5_Tokenizer::ENDTAG &&
-                $token['name'] === 'html') {
-                    $this->mode = self::AFTER_AFTER_FRAMESET;
-
-                /* A start tag with the tag name "noframes" */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG &&
-                $token['name'] === 'noframes') {
-                    $this->processWithRulesFor($token, self::IN_HEAD);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    /* Stop parsing */
-
-                /* Anything else */
-                } else {
-                    /* Parse error. Ignore the token. */
-                    $this->ignored = true;
-                }
-            break;
-
-            case self::AFTER_AFTER_BODY:
-                /* A comment token */
-                if ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the Document object with the data
-                    attribute set to the data given in the comment token. */
-                    // XDOM
-                    $comment = $this->dom->createComment($token['data']);
-                    $this->dom->appendChild($comment);
-
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
-                $token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
-                ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
-                    $this->processWithRulesFor($token, self::IN_BODY);
-
-                /* An end-of-file token */
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    /* OMG DONE!! */
-                } else {
-                    // parse error
-                    $this->mode = self::IN_BODY;
-                    $this->emitToken($token);
-                }
-            break;
-
-            case self::AFTER_AFTER_FRAMESET:
-                /* A comment token */
-                if ($token['type'] === HTML5_Tokenizer::COMMENT) {
-                    /* Append a Comment node to the Document object with the data
-                    attribute set to the data given in the comment token. */
-                    // XDOM
-                    $comment = $this->dom->createComment($token['data']);
-                    $this->dom->appendChild($comment);
-                } elseif ($token['type'] === HTML5_Tokenizer::DOCTYPE ||
-                $token['type'] === HTML5_Tokenizer::SPACECHARACTER ||
-                ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'html')) {
-                    $this->processWithRulesFor($token, self::IN_BODY);
-
-                /* An end-of-file token */
-                } elseif ($token['type'] === HTML5_Tokenizer::EOF) {
-                    /* OMG DONE!! */
-                } elseif ($token['type'] === HTML5_Tokenizer::STARTTAG && $token['name'] === 'nofrmaes') {
-                    $this->processWithRulesFor($token, self::IN_HEAD);
-                } else {
-                    // parse error
-                }
-            break;
-        }
-    }
-
-    private function insertElement($token, $append = true) {
-        $el = $this->dom->createElementNS(self::NS_HTML, $token['name']);
-
-        if (!empty($token['attr'])) {
-            foreach ($token['attr'] as $attr) {
-                if (!$el->hasAttribute($attr['name']) && preg_match("/^[a-zA-Z_:]/", $attr['name'])) {
-                    $el->setAttribute($attr['name'], $attr['value']);
-                }
-            }
-        }
-        if ($append) {
-            $this->appendToRealParent($el);
-            $this->stack[] = $el;
-        }
-
-        return $el;
-    }
-
-    /**
-     * @param $data
-     */
-    private function insertText($data) {
-        if ($data === '') {
-            return;
-        }
-        if ($this->ignore_lf_token) {
-            if ($data[0] === "\n") {
-                $data = substr($data, 1);
-                if ($data === false) {
-                    return;
-                }
-            }
-        }
-        $text = $this->dom->createTextNode($data);
-        $this->appendToRealParent($text);
-    }
-
-    /**
-     * @param $data
-     */
-    private function insertComment($data) {
-        $comment = $this->dom->createComment($data);
-        $this->appendToRealParent($comment);
-    }
-
-    /**
-     * @param $node
-     */
-    private function appendToRealParent($node) {
-        // this is only for the foster_parent case
-        /* If the current node is a table, tbody, tfoot, thead, or tr
-        element, then, whenever a node would be inserted into the current
-        node, it must instead be inserted into the foster parent element. */
-        if (
-            !$this->foster_parent ||
-            !in_array(
-                end($this->stack)->tagName,
-                array('table', 'tbody', 'tfoot', 'thead', 'tr')
-            )
-        ) {
-            end($this->stack)->appendChild($node);
-        } else {
-            $this->fosterParent($node);
-        }
-    }
-
-    /**
-     * @param $el
-     * @param int $scope
-     * @return bool|null
-     */
-    private function elementInScope($el, $scope = self::SCOPE) {
-        if (is_array($el)) {
-            foreach($el as $element) {
-                if ($this->elementInScope($element, $scope)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        $leng = count($this->stack);
-
-        for ($n = 0; $n < $leng; $n++) {
-            /* 1. Initialise node to be the current node (the bottommost node of
-            the stack). */
-            $node = $this->stack[$leng - 1 - $n];
-
-            if ($node->tagName === $el) {
-                /* 2. If node is the target node, terminate in a match state. */
-                return true;
-
-                // We've expanded the logic for these states a little differently;
-                // Hixie's refactoring into "specific scope" is more general, but
-                // this "gets the job done"
-
-            // these are the common states for all scopes
-            } elseif ($node->tagName === 'table' || $node->tagName === 'html') {
-                return false;
-
-            // these are valid for "in scope" and "in list item scope"
-            } elseif ($scope !== self::SCOPE_TABLE &&
-            (in_array($node->tagName, array('applet', 'caption', 'td',
-                'th', 'button', 'marquee', 'object')) ||
-                $node->tagName === 'foreignObject' && $node->namespaceURI === self::NS_SVG)) {
-                return false;
-
-
-            // these are valid for "in list item scope"
-            } elseif ($scope === self::SCOPE_LISTITEM && in_array($node->tagName, array('ol', 'ul'))) {
-                return false;
-            }
-
-            /* Otherwise, set node to the previous entry in the stack of open
-            elements and return to step 2. (This will never fail, since the loop
-            will always terminate in the previous step if the top of the stack
-            is reached.) */
-        }
-
-        // To fix warning. This never happens or should return true/false
-        return null;
-    }
-
-    /**
-     * @return bool
-     */
-    private function reconstructActiveFormattingElements() {
-        /* 1. If there are no entries in the list of active formatting elements,
-        then there is nothing to reconstruct; stop this algorithm. */
-        $formatting_elements = count($this->a_formatting);
-
-        if ($formatting_elements === 0) {
-            return false;
-        }
-
-        /* 3. Let entry be the last (most recently added) element in the list
-        of active formatting elements. */
-        $entry = end($this->a_formatting);
-
-        /* 2. If the last (most recently added) entry in the list of active
-        formatting elements is a marker, or if it is an element that is in the
-        stack of open elements, then there is nothing to reconstruct; stop this
-        algorithm. */
-        if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
-            return false;
-        }
-
-        for ($a = $formatting_elements - 1; $a >= 0; true) {
-            /* 4. If there are no entries before entry in the list of active
-            formatting elements, then jump to step 8. */
-            if ($a === 0) {
-                $step_seven = false;
-                break;
-            }
-
-            /* 5. Let entry be the entry one earlier than entry in the list of
-            active formatting elements. */
-            $a--;
-            $entry = $this->a_formatting[$a];
-
-            /* 6. If entry is neither a marker nor an element that is also in
-            thetack of open elements, go to step 4. */
-            if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
-                break;
-            }
-        }
-
-        while (true) {
-            /* 7. Let entry be the element one later than entry in the list of
-            active formatting elements. */
-            if (isset($step_seven) && $step_seven === true) {
-                $a++;
-                $entry = $this->a_formatting[$a];
-            }
-
-            /* 8. Perform a shallow clone of the element entry to obtain clone. */
-            $clone = $entry->cloneNode();
-
-            /* 9. Append clone to the current node and push it onto the stack
-            of open elements  so that it is the new current node. */
-            $this->appendToRealParent($clone);
-            $this->stack[] = $clone;
-
-            /* 10. Replace the entry for entry in the list with an entry for
-            clone. */
-            $this->a_formatting[$a] = $clone;
-
-            /* 11. If the entry for clone in the list of active formatting
-            elements is not the last entry in the list, return to step 7. */
-            if (end($this->a_formatting) !== $clone) {
-                $step_seven = true;
-            } else {
-                break;
-            }
-        }
-
-        // Return value not in use ATM. Would just make sense to also return true here.
-        return true;
-    }
-
-    /**
-     *
-     */
-    private function clearTheActiveFormattingElementsUpToTheLastMarker() {
-        /* When the steps below require the UA to clear the list of active
-        formatting elements up to the last marker, the UA must perform the
-        following steps: */
-
-        while (true) {
-            /* 1. Let entry be the last (most recently added) entry in the list
-            of active formatting elements. */
-            $entry = end($this->a_formatting);
-
-            /* 2. Remove entry from the list of active formatting elements. */
-            array_pop($this->a_formatting);
-
-            /* 3. If entry was a marker, then stop the algorithm at this point.
-            The list has been cleared up to the last marker. */
-            if ($entry === self::MARKER) {
-                break;
-            }
-        }
-    }
-
-    /**
-     * @param array $exclude
-     */
-    private function generateImpliedEndTags($exclude = array()) {
-        /* When the steps below require the UA to generate implied end tags,
-         * then, while the current node is a dc element, a dd element, a ds
-         * element, a dt element, an li element, an option element, an optgroup
-         * element, a p element, an rp element, or an rt element, the UA must
-         * pop the current node off the stack of open elements. */
-        $node = end($this->stack);
-        $elements = array_diff(array('dc', 'dd', 'ds', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
-
-        while (in_array(end($this->stack)->tagName, $elements)) {
-            array_pop($this->stack);
-        }
-    }
-
-    /**
-     * @param $node
-     * @return int
-     */
-    private function getElementCategory($node) {
-        if (!is_object($node)) {
-            debug_print_backtrace();
-        }
-        $name = $node->tagName;
-        if (in_array($name, $this->special)) {
-            return self::SPECIAL;
-        } elseif (in_array($name, $this->scoping)) {
-            return self::SCOPING;
-        } elseif (in_array($name, $this->formatting)) {
-            return self::FORMATTING;
-        } else {
-            return self::PHRASING;
-        }
-    }
-
-    /**
-     * @param $elements
-     */
-    private function clearStackToTableContext($elements) {
-        /* When the steps above require the UA to clear the stack back to a
-        table context, it means that the UA must, while the current node is not
-        a table element or an html element, pop elements from the stack of open
-        elements. */
-        while (true) {
-            $name = end($this->stack)->tagName;
-
-            if (in_array($name, $elements)) {
-                break;
-            } else {
-                array_pop($this->stack);
-            }
-        }
-    }
-
-    /**
-     * @param null $context
-     */
-    private function resetInsertionMode($context = null) {
-        /* 1. Let last be false. */
-        $last = false;
-        $leng = count($this->stack);
-
-        for ($n = $leng - 1; $n >= 0; $n--) {
-            /* 2. Let node be the last node in the stack of open elements. */
-            $node = $this->stack[$n];
-
-            /* 3. If node is the first node in the stack of open elements, then
-             * set last to true and set node to the context  element. (fragment
-             * case) */
-            if ($this->stack[0]->isSameNode($node)) {
-                $last = true;
-                $node = $context;
-            }
-
-            /* 4. If node is a select element, then switch the insertion mode to
-            "in select" and abort these steps. (fragment case) */
-            if ($node->tagName === 'select') {
-                $this->mode = self::IN_SELECT;
-                break;
-
-            /* 5. If node is a td or th element, then switch the insertion mode
-            to "in cell" and abort these steps. */
-            } elseif ($node->tagName === 'td' || $node->nodeName === 'th') {
-                $this->mode = self::IN_CELL;
-                break;
-
-            /* 6. If node is a tr element, then switch the insertion mode to
-            "in    row" and abort these steps. */
-            } elseif ($node->tagName === 'tr') {
-                $this->mode = self::IN_ROW;
-                break;
-
-            /* 7. If node is a tbody, thead, or tfoot element, then switch the
-            insertion mode to "in table body" and abort these steps. */
-            } elseif (in_array($node->tagName, array('tbody', 'thead', 'tfoot'))) {
-                $this->mode = self::IN_TABLE_BODY;
-                break;
-
-            /* 8. If node is a caption element, then switch the insertion mode
-            to "in caption" and abort these steps. */
-            } elseif ($node->tagName === 'caption') {
-                $this->mode = self::IN_CAPTION;
-                break;
-
-            /* 9. If node is a colgroup element, then switch the insertion mode
-            to "in column group" and abort these steps. (innerHTML case) */
-            } elseif ($node->tagName === 'colgroup') {
-                $this->mode = self::IN_COLUMN_GROUP;
-                break;
-
-            /* 10. If node is a table element, then switch the insertion mode
-            to "in table" and abort these steps. */
-            } elseif ($node->tagName === 'table') {
-                $this->mode = self::IN_TABLE;
-                break;
-
-            /* 11. If node is an element from the MathML namespace or the SVG
-             * namespace, then switch the insertion mode to "in foreign
-             * content", let the secondary insertion mode be "in body", and
-             * abort these steps. */
-            } elseif ($node->namespaceURI === self::NS_SVG ||
-            $node->namespaceURI === self::NS_MATHML) {
-                $this->mode = self::IN_FOREIGN_CONTENT;
-                $this->secondary_mode = self::IN_BODY;
-                break;
-
-            /* 12. If node is a head element, then switch the insertion mode
-            to "in body" ("in body"! not "in head"!) and abort these steps.
-            (fragment case) */
-            } elseif ($node->tagName === 'head') {
-                $this->mode = self::IN_BODY;
-                break;
-
-            /* 13. If node is a body element, then switch the insertion mode to
-            "in body" and abort these steps. */
-            } elseif ($node->tagName === 'body') {
-                $this->mode = self::IN_BODY;
-                break;
-
-            /* 14. If node is a frameset element, then switch the insertion
-            mode to "in frameset" and abort these steps. (fragment case) */
-            } elseif ($node->tagName === 'frameset') {
-                $this->mode = self::IN_FRAMESET;
-                break;
-
-            /* 15. If node is an html element, then: if the head element
-            pointer is null, switch the insertion mode to "before head",
-            otherwise, switch the insertion mode to "after head". In either
-            case, abort these steps. (fragment case) */
-            } elseif ($node->tagName === 'html') {
-                $this->mode = ($this->head_pointer === null)
-                    ? self::BEFORE_HEAD
-                    : self::AFTER_HEAD;
-
-                break;
-
-            /* 16. If last is true, then set the insertion mode to "in body"
-            and    abort these steps. (fragment case) */
-            } elseif ($last) {
-                $this->mode = self::IN_BODY;
-                break;
-            }
-        }
-    }
-
-    /**
-     *
-     */
-    private function closeCell() {
-        /* If the stack of open elements has a td or th element in table scope,
-        then act as if an end tag token with that tag name had been seen. */
-        foreach (array('td', 'th') as $cell) {
-            if ($this->elementInScope($cell, self::SCOPE_TABLE)) {
-                $this->emitToken(array(
-                    'name' => $cell,
-                    'type' => HTML5_Tokenizer::ENDTAG
-                ));
-
-                break;
-            }
-        }
-    }
-
-    /**
-     * @param $token
-     * @param $mode
-     */
-    private function processWithRulesFor($token, $mode) {
-        /* "using the rules for the m insertion mode", where m is one of these
-         * modes, the user agent must use the rules described under the m
-         * insertion mode's section, but must leave the insertion mode
-         * unchanged unless the rules in m themselves switch the insertion mode
-         * to a new value. */
-        $this->emitToken($token, $mode);
-    }
-
-    /**
-     * @param $token
-     */
-    private function insertCDATAElement($token) {
-        $this->insertElement($token);
-        $this->original_mode = $this->mode;
-        $this->mode = self::IN_CDATA_RCDATA;
-        $this->content_model = HTML5_Tokenizer::CDATA;
-    }
-
-    /**
-     * @param $token
-     */
-    private function insertRCDATAElement($token) {
-        $this->insertElement($token);
-        $this->original_mode = $this->mode;
-        $this->mode = self::IN_CDATA_RCDATA;
-        $this->content_model = HTML5_Tokenizer::RCDATA;
-    }
-
-    /**
-     * @param $token
-     * @param $key
-     * @return bool
-     */
-    private function getAttr($token, $key) {
-        if (!isset($token['attr'])) {
-            return false;
-        }
-        $ret = false;
-        foreach ($token['attr'] as $keypair) {
-            if ($keypair['name'] === $key) {
-                $ret = $keypair['value'];
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * @return mixed
-     */
-    private function getCurrentTable() {
-        /* The current table is the last table  element in the stack of open
-         * elements, if there is one. If there is no table element in the stack
-         * of open elements (fragment case), then the current table is the
-         * first element in the stack of open elements (the html element). */
-        for ($i = count($this->stack) - 1; $i >= 0; $i--) {
-            if ($this->stack[$i]->tagName === 'table') {
-                return $this->stack[$i];
-            }
-        }
-        return $this->stack[0];
-    }
-
-    /**
-     * @return mixed
-     */
-    private function getFosterParent() {
-        /* The foster parent element is the parent element of the last
-        table element in the stack of open elements, if there is a
-        table element and it has such a parent element. If there is no
-        table element in the stack of open elements (innerHTML case),
-        then the foster parent element is the first element in the
-        stack of open elements (the html  element). Otherwise, if there
-        is a table element in the stack of open elements, but the last
-        table element in the stack of open elements has no parent, or
-        its parent node is not an element, then the foster parent
-        element is the element before the last table element in the
-        stack of open elements. */
-        for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-            if ($this->stack[$n]->tagName === 'table') {
-                $table = $this->stack[$n];
-                break;
-            }
-        }
-
-        if (isset($table) && $table->parentNode !== null) {
-            return $table->parentNode;
-
-        } elseif (!isset($table)) {
-            return $this->stack[0];
-
-        } elseif (isset($table) && ($table->parentNode === null ||
-        $table->parentNode->nodeType !== XML_ELEMENT_NODE)) {
-            return $this->stack[$n - 1];
-        }
-
-        return null;
-    }
-
-    /**
-     * @param $node
-     */
-    public function fosterParent($node) {
-        $foster_parent = $this->getFosterParent();
-        $table = $this->getCurrentTable(); // almost equivalent to last table element, except it can be html
-        /* When a node node is to be foster parented, the node node must be
-         * be inserted into the foster parent element. */
-        /* If the foster parent element is the parent element of the last table
-         * element in the stack of open elements, then node must be inserted
-         * immediately before the last table element in the stack of open
-         * elements in the foster parent element; otherwise, node must be
-         * appended to the foster parent element. */
-        if ($table->tagName === 'table' && $table->parentNode->isSameNode($foster_parent)) {
-            $foster_parent->insertBefore($node, $table);
-        } else {
-            $foster_parent->appendChild($node);
-        }
-    }
-
-    /**
-     * For debugging, prints the stack
-     */
-    private function printStack() {
-        $names = array();
-        foreach ($this->stack as $i => $element) {
-            $names[] = $element->tagName;
-        }
-        echo "  -> stack [" . implode(', ', $names) . "]\n";
-    }
-
-    /**
-     * For debugging, prints active formatting elements
-     */
-    private function printActiveFormattingElements() {
-        if (!$this->a_formatting) {
-            return;
-        }
-        $names = array();
-        foreach ($this->a_formatting as $node) {
-            if ($node === self::MARKER) {
-                $names[] = 'MARKER';
-            } else {
-                $names[] = $node->tagName;
-            }
-        }
-        echo "  -> active formatting [" . implode(', ', $names) . "]\n";
-    }
-
-    /**
-     * @return bool
-     */
-    public function currentTableIsTainted() {
-        return !empty($this->getCurrentTable()->tainted);
-    }
-
-    /**
-     * Sets up the tree constructor for building a fragment.
-     *
-     * @param null $context
-     */
-    public function setupContext($context = null) {
-        $this->fragment = true;
-        if ($context) {
-            $context = $this->dom->createElementNS(self::NS_HTML, $context);
-            /* 4.1. Set the HTML parser's tokenization  stage's content model
-             * flag according to the context element, as follows: */
-            switch ($context->tagName) {
-                case 'title': case 'textarea':
-                    $this->content_model = HTML5_Tokenizer::RCDATA;
-                    break;
-                case 'style': case 'script': case 'xmp': case 'iframe':
-                case 'noembed': case 'noframes':
-                    $this->content_model = HTML5_Tokenizer::CDATA;
-                    break;
-                case 'noscript':
-                    // XSCRIPT: assuming scripting is enabled
-                    $this->content_model = HTML5_Tokenizer::CDATA;
-                    break;
-                case 'plaintext':
-                    $this->content_model = HTML5_Tokenizer::PLAINTEXT;
-                    break;
-            }
-            /* 4.2. Let root be a new html element with no attributes. */
-            $root = $this->dom->createElementNS(self::NS_HTML, 'html');
-            $this->root = $root;
-            /* 4.3 Append the element root to the Document node created above. */
-            $this->dom->appendChild($root);
-            /* 4.4 Set up the parser's stack of open elements so that it
-             * contains just the single element root. */
-            $this->stack = array($root);
-            /* 4.5 Reset the parser's insertion mode appropriately. */
-            $this->resetInsertionMode($context);
-            /* 4.6 Set the parser's form element pointer  to the nearest node
-             * to the context element that is a form element (going straight up
-             * the ancestor chain, and including the element itself, if it is a
-             * form element), or, if there is no such form element, to null. */
-            $node = $context;
-            do {
-                if ($node->tagName === 'form') {
-                    $this->form_pointer = $node;
-                    break;
-                }
-            } while ($node = $node->parentNode);
-        }
-    }
-
-    /**
-     * @param $token
-     * @return mixed
-     */
-    public function adjustMathMLAttributes($token) {
-        foreach ($token['attr'] as &$kp) {
-            if ($kp['name'] === 'definitionurl') {
-                $kp['name'] = 'definitionURL';
-            }
-        }
-        return $token;
-    }
-
-    /**
-     * @param $token
-     * @return mixed
-     */
-    public function adjustSVGAttributes($token) {
-        static $lookup = array(
-            'attributename' => 'attributeName',
-            'attributetype' => 'attributeType',
-            'basefrequency' => 'baseFrequency',
-            'baseprofile' => 'baseProfile',
-            'calcmode' => 'calcMode',
-            'clippathunits' => 'clipPathUnits',
-            'contentscripttype' => 'contentScriptType',
-            'contentstyletype' => 'contentStyleType',
-            'diffuseconstant' => 'diffuseConstant',
-            'edgemode' => 'edgeMode',
-            'externalresourcesrequired' => 'externalResourcesRequired',
-            'filterres' => 'filterRes',
-            'filterunits' => 'filterUnits',
-            'glyphref' => 'glyphRef',
-            'gradienttransform' => 'gradientTransform',
-            'gradientunits' => 'gradientUnits',
-            'kernelmatrix' => 'kernelMatrix',
-            'kernelunitlength' => 'kernelUnitLength',
-            'keypoints' => 'keyPoints',
-            'keysplines' => 'keySplines',
-            'keytimes' => 'keyTimes',
-            'lengthadjust' => 'lengthAdjust',
-            'limitingconeangle' => 'limitingConeAngle',
-            'markerheight' => 'markerHeight',
-            'markerunits' => 'markerUnits',
-            'markerwidth' => 'markerWidth',
-            'maskcontentunits' => 'maskContentUnits',
-            'maskunits' => 'maskUnits',
-            'numoctaves' => 'numOctaves',
-            'pathlength' => 'pathLength',
-            'patterncontentunits' => 'patternContentUnits',
-            'patterntransform' => 'patternTransform',
-            'patternunits' => 'patternUnits',
-            'pointsatx' => 'pointsAtX',
-            'pointsaty' => 'pointsAtY',
-            'pointsatz' => 'pointsAtZ',
-            'preservealpha' => 'preserveAlpha',
-            'preserveaspectratio' => 'preserveAspectRatio',
-            'primitiveunits' => 'primitiveUnits',
-            'refx' => 'refX',
-            'refy' => 'refY',
-            'repeatcount' => 'repeatCount',
-            'repeatdur' => 'repeatDur',
-            'requiredextensions' => 'requiredExtensions',
-            'requiredfeatures' => 'requiredFeatures',
-            'specularconstant' => 'specularConstant',
-            'specularexponent' => 'specularExponent',
-            'spreadmethod' => 'spreadMethod',
-            'startoffset' => 'startOffset',
-            'stddeviation' => 'stdDeviation',
-            'stitchtiles' => 'stitchTiles',
-            'surfacescale' => 'surfaceScale',
-            'systemlanguage' => 'systemLanguage',
-            'tablevalues' => 'tableValues',
-            'targetx' => 'targetX',
-            'targety' => 'targetY',
-            'textlength' => 'textLength',
-            'viewbox' => 'viewBox',
-            'viewtarget' => 'viewTarget',
-            'xchannelselector' => 'xChannelSelector',
-            'ychannelselector' => 'yChannelSelector',
-            'zoomandpan' => 'zoomAndPan',
-        );
-        foreach ($token['attr'] as &$kp) {
-            if (isset($lookup[$kp['name']])) {
-                $kp['name'] = $lookup[$kp['name']];
-            }
-        }
-        return $token;
-    }
-
-    /**
-     * @param $token
-     * @return mixed
-     */
-    public function adjustForeignAttributes($token) {
-        static $lookup = array(
-            'xlink:actuate' => array('xlink', 'actuate', self::NS_XLINK),
-            'xlink:arcrole' => array('xlink', 'arcrole', self::NS_XLINK),
-            'xlink:href' => array('xlink', 'href', self::NS_XLINK),
-            'xlink:role' => array('xlink', 'role', self::NS_XLINK),
-            'xlink:show' => array('xlink', 'show', self::NS_XLINK),
-            'xlink:title' => array('xlink', 'title', self::NS_XLINK),
-            'xlink:type' => array('xlink', 'type', self::NS_XLINK),
-            'xml:base' => array('xml', 'base', self::NS_XML),
-            'xml:lang' => array('xml', 'lang', self::NS_XML),
-            'xml:space' => array('xml', 'space', self::NS_XML),
-            'xmlns' => array(null, 'xmlns', self::NS_XMLNS),
-            'xmlns:xlink' => array('xmlns', 'xlink', self::NS_XMLNS),
-        );
-        foreach ($token['attr'] as &$kp) {
-            if (isset($lookup[$kp['name']])) {
-                $kp['name'] = $lookup[$kp['name']];
-            }
-        }
-        return $token;
-    }
-
-    /**
-     * @param $token
-     * @param $namespaceURI
-     */
-    public function insertForeignElement($token, $namespaceURI) {
-        $el = $this->dom->createElementNS($namespaceURI, $token['name']);
-
-        if (!empty($token['attr'])) {
-            foreach ($token['attr'] as $kp) {
-                $attr = $kp['name'];
-                if (is_array($attr)) {
-                    $ns = $attr[2];
-                    $attr = $attr[1];
-                } else {
-                    $ns = self::NS_HTML;
-                }
-                if (!$el->hasAttributeNS($ns, $attr)) {
-                    // XSKETCHY: work around godawful libxml bug
-                    if ($ns === self::NS_XLINK) {
-                        $el->setAttribute('xlink:'.$attr, $kp['value']);
-                    } elseif ($ns === self::NS_HTML) {
-                        // Another godawful libxml bug
-                        $el->setAttribute($attr, $kp['value']);
-                    } else {
-                        $el->setAttributeNS($ns, $attr, $kp['value']);
-                    }
-                }
-            }
-        }
-        $this->appendToRealParent($el);
-        $this->stack[] = $el;
-        // XERROR: see below
-        /* If the newly created element has an xmlns attribute in the XMLNS
-         * namespace  whose value is not exactly the same as the element's
-         * namespace, that is a parse error. Similarly, if the newly created
-         * element has an xmlns:xlink attribute in the XMLNS namespace whose
-         * value is not the XLink Namespace, that is a parse error. */
-    }
-
-    /**
-     * @return DOMDocument|DOMNodeList
-     */
-    public function save() {
-        $this->dom->normalize();
-        if (!$this->fragment) {
-            return $this->dom;
-        } else {
-            if ($this->root) {
-                return $this->root->childNodes;
-            } else {
-                return $this->dom->childNodes;
-            }
-        }
-    }
-}
-
diff --git a/library/vendor/dompdf/lib/html5lib/named-character-references.ser b/library/vendor/dompdf/lib/html5lib/named-character-references.ser
deleted file mode 100644
index e3ae05020..000000000
--- a/library/vendor/dompdf/lib/html5lib/named-character-references.ser
+++ /dev/null
@@ -1 +0,0 @@
-a:52:{s:1:"A";a:16:{s:1:"E";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:198;}s:9:"codepoint";i:198;}}}}s:1:"M";a:1:{s:1:"P";a:2:{s:1:";";a:1:{s:9:"codepoint";i:38;}s:9:"codepoint";i:38;}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:193;}s:9:"codepoint";i:193;}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:258;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:194;}s:9:"codepoint";i:194;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1040;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120068;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:192;}s:9:"codepoint";i:192;}}}}}s:1:"l";a:1:{s:1:"p";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:913;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:256;}}}}}s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10835;}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:260;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120120;}}}}s:1:"p";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"y";a:1:{s:1:"F";a:1:{s:1:"u";a:1:{s:1:"n";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8289;}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:197;}s:9:"codepoint";i:197;}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119964;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8788;}}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:195;}s:9:"codepoint";i:195;}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:196;}s:9:"codepoint";i:196;}}}}s:1:"B";a:8:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"s";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}}}}s:1:"r";a:2:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10983;}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8966;}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1041;}}}s:1:"e";a:3:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8757;}}}}}}s:1:"r";a:1:{s:1:"n";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8492;}}}}}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:914;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120069;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120121;}}}}s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:728;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8492;}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8782;}}}}}}}s:1:"C";a:14:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1063;}}}}s:1:"O";a:1:{s:1:"P";a:1:{s:1:"Y";a:2:{s:1:";";a:1:{s:9:"codepoint";i:169;}s:9:"codepoint";i:169;}}}s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:262;}}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8914;}s:1:"i";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:"i";a:1:{s:1:"f";a:1:{s:1:"f";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8517;}}}}}}}}}}}}}}}}}}}s:1:"y";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"y";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8493;}}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:268;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:199;}s:9:"codepoint";i:199;}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:264;}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8752;}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:266;}}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:184;}}}}}}s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:183;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8493;}}}s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:935;}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"l";a:1:{s:1:"e";a:4:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8857;}}}}s:1:"M";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8854;}}}}}}s:1:"P";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8853;}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8855;}}}}}}}}}}}s:1:"l";a:1:{s:1:"o";a:2:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"w";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8754;}}}}}}}}}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"C";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8221;}}}}}}}}}}}}s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8217;}}}}}}}}}}}}}}}s:1:"o";a:4:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8759;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10868;}}}}}s:1:"n";a:3:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8801;}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8751;}}}}s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8750;}}}}}}}}}}}}}}s:1:"p";a:2:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8450;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:"u";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8720;}}}}}}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"C";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"w";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8755;}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10799;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119966;}}}}s:1:"u";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8915;}s:1:"C";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8781;}}}}}}}s:1:"D";a:11:{s:1:"D";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8517;}s:1:"o";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"h";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10513;}}}}}}}}s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1026;}}}}s:1:"S";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1029;}}}}s:1:"Z";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1039;}}}}s:1:"a";a:3:{s:1:"g";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8225;}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8609;}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10980;}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:270;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1044;}}}s:1:"e";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8711;}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:916;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120071;}}}s:1:"i";a:2:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:4:{s:1:"A";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:180;}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:729;}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"A";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:733;}}}}}}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:96;}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:732;}}}}}}}}}}}}}}s:1:"m";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8900;}}}}}}s:1:"f";a:1:{s:1:"f";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8518;}}}}}}}}}}}}}s:1:"o";a:4:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120123;}}}s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:168;}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8412;}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8784;}}}}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:6:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8751;}}}}}}}}}}}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:168;}}s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8659;}}}}}}}}}}s:1:"L";a:2:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8656;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10980;}}}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:2:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10232;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10234;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10233;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8872;}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8657;}}}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8661;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:6:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8595;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10515;}}}}s:1:"U";a:1:{s:1:"p";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8693;}}}}}}}}}}}}}s:1:"B";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:785;}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:3:{s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10576;}}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10590;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8637;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10582;}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10591;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8641;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10583;}}}}}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8868;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8615;}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8659;}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119967;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:272;}}}}}}}s:1:"E";a:16:{s:1:"N";a:1:{s:1:"G";a:1:{s:1:";";a:1:{s:9:"codepoint";i:330;}}}s:1:"T";a:1:{s:1:"H";a:2:{s:1:";";a:1:{s:9:"codepoint";i:208;}s:9:"codepoint";i:208;}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:201;}s:9:"codepoint";i:201;}}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:282;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:202;}s:9:"codepoint";i:202;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1069;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:278;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120072;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:200;}s:9:"codepoint";i:200;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8712;}}}}}}}s:1:"m";a:2:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:274;}}}}s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:2:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9723;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9643;}}}}}}}}}}}}}}}}}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:280;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120124;}}}}s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:917;}}}}}}}s:1:"q";a:1:{s:1:"u";a:2:{s:1:"a";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10869;}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8770;}}}}}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8652;}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8496;}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10867;}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:919;}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:203;}s:9:"codepoint";i:203;}}}s:1:"x";a:2:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8707;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8519;}}}}}}}}}}}}}s:1:"F";a:5:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1060;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120073;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"d";a:2:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9724;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"S";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"S";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}}}}}}}}}}}}}}}}}}s:1:"o";a:3:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120125;}}}s:1:"r";a:1:{s:1:"A";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8704;}}}}}s:1:"u";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8497;}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8497;}}}}}s:1:"G";a:12:{s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1027;}}}}s:1:"T";a:2:{s:1:";";a:1:{s:9:"codepoint";i:62;}s:9:"codepoint";i:62;}s:1:"a";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:915;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:988;}}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:286;}}}}}}s:1:"c";a:3:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:290;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:284;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1043;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:288;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120074;}}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8921;}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120126;}}}}s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:6:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8805;}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8923;}}}}}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8807;}}}}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10914;}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8823;}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10878;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8819;}}}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119970;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8811;}}}s:1:"H";a:8:{s:1:"A";a:1:{s:1:"R";a:1:{s:1:"D";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1066;}}}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:711;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:94;}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:292;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8460;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"b";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8459;}}}}}}}}}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8461;}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"z";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"L";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9472;}}}}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8459;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:294;}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"p";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"H";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8782;}}}}}}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8783;}}}}}}}}}}s:1:"I";a:14:{s:1:"E";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1045;}}}}s:1:"J";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:306;}}}}}s:1:"O";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1025;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:205;}s:9:"codepoint";i:205;}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:206;}s:9:"codepoint";i:206;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1048;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:304;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8465;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:204;}s:9:"codepoint";i:204;}}}}}s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8465;}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:298;}}}s:1:"g";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"I";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8520;}}}}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}}}}}s:1:"n";a:2:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8748;}s:1:"e";a:2:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8747;}}}}}s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8898;}}}}}}}}}}}s:1:"v";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"C";a:1:{s:1:"o";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8291;}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8290;}}}}}}}}}}}}}}s:1:"o";a:3:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:302;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120128;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:921;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8464;}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:296;}}}}}}s:1:"u";a:2:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1030;}}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:207;}s:9:"codepoint";i:207;}}}}s:1:"J";a:5:{s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:308;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1049;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120077;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120129;}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119973;}}}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1032;}}}}}}s:1:"u";a:1:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1028;}}}}}}s:1:"K";a:7:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1061;}}}}s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1036;}}}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:922;}}}}}s:1:"c";a:2:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:310;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1050;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120078;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120130;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119974;}}}}}s:1:"L";a:11:{s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1033;}}}}s:1:"T";a:2:{s:1:";";a:1:{s:9:"codepoint";i:60;}s:9:"codepoint";i:60;}s:1:"a";a:5:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:313;}}}}}s:1:"m";a:1:{s:1:"b";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:923;}}}}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10218;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8466;}}}}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8606;}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:317;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:315;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1051;}}}s:1:"e";a:2:{s:1:"f";a:1:{s:1:"t";a:10:{s:1:"A";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10216;}}}}}}}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8592;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8676;}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8646;}}}}}}}}}}}}}}}}s:1:"C";a:1:{s:1:"e";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8968;}}}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10214;}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:2:{s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10593;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8643;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10585;}}}}}}}}}}}}}}s:1:"F";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8970;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8596;}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10574;}}}}}}}}}}}}s:1:"T";a:2:{s:1:"e";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8867;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8612;}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10586;}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8882;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10703;}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8884;}}}}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:3:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10577;}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10592;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8639;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10584;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8636;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10578;}}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8656;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"s";a:6:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8922;}}}}}}}}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8806;}}}}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8822;}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10913;}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10877;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8818;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120079;}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8920;}s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8666;}}}}}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:319;}}}}}}s:1:"o";a:3:{s:1:"n";a:1:{s:1:"g";a:4:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10229;}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10231;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10230;}}}}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10232;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10234;}}}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10233;}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120131;}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8601;}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8600;}}}}}}}}}}}}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8466;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8624;}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:321;}}}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8810;}}}s:1:"M";a:8:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10501;}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1052;}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8287;}}}}}}}}}}s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8499;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120080;}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"P";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8723;}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120132;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8499;}}}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:924;}}}s:1:"N";a:9:{s:1:"J";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1034;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:323;}}}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:327;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:325;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1053;}}}s:1:"e";a:3:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"v";a:1:{s:1:"e";a:3:{s:1:"M";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}}}s:1:"T";a:1:{s:1:"h";a:1:{s:1:"i";a:2:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"y";a:1:{s:1:"T";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"d";a:2:{s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8811;}}}}}}}}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8810;}}}}}}}}}}}}}s:1:"w";a:1:{s:1:"L";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120081;}}}s:1:"o";a:4:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8288;}}}}}}s:1:"n";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"k";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:160;}}}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8469;}}}s:1:"t";a:11:{s:1:";";a:1:{s:9:"codepoint";i:10988;}s:1:"C";a:2:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8802;}}}}}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"C";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8813;}}}}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}}}}}}}}}}}}}}}s:1:"E";a:3:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8713;}}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8800;}}}}}s:1:"x";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8708;}}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8815;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8817;}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8825;}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8821;}}}}}}}}}}}}}s:1:"L";a:1:{s:1:"e";a:2:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"T";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8938;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8940;}}}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8814;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8816;}}}}}}s:1:"G";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8824;}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8820;}}}}}}}}}}s:1:"P";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8832;}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8928;}}}}}}}}}}}}}}}}}}}s:1:"R";a:2:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"E";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8716;}}}}}}}}}}}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"T";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8939;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8941;}}}}}}}}}}}}}}}}}}}s:1:"S";a:2:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"S";a:1:{s:1:"u";a:2:{s:1:"b";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8930;}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8931;}}}}}}}}}}}}}}}}}}}s:1:"u";a:3:{s:1:"b";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8840;}}}}}}}}}}s:1:"c";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8833;}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8929;}}}}}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8841;}}}}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8769;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8772;}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8775;}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8777;}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119977;}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:209;}s:9:"codepoint";i:209;}}}}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:925;}}}s:1:"O";a:14:{s:1:"E";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:338;}}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:211;}s:9:"codepoint";i:211;}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:212;}s:9:"codepoint";i:212;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1054;}}}s:1:"d";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:336;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120082;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:210;}s:9:"codepoint";i:210;}}}}}s:1:"m";a:3:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:332;}}}}s:1:"e";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:937;}}}}s:1:"i";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:927;}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120134;}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"C";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8220;}}}}}}}}}}}}s:1:"Q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8216;}}}}}}}}}}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10836;}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119978;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:216;}s:9:"codepoint";i:216;}}}}}s:1:"t";a:1:{s:1:"i";a:2:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:213;}s:9:"codepoint";i:213;}}}s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10807;}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:214;}s:9:"codepoint";i:214;}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"B";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:175;}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9182;}}s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9140;}}}}}}}}s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9180;}}}}}}}}}}}}}}}}s:1:"P";a:9:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8706;}}}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1055;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120083;}}}s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:934;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:928;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"M";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:177;}}}}}}}}}s:1:"o";a:2:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8460;}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8473;}}}}s:1:"r";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10939;}s:1:"e";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8826;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10927;}}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8828;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8830;}}}}}}}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8243;}}}}s:1:"o";a:2:{s:1:"d";a:1:{s:1:"u";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8719;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8759;}s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119979;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:936;}}}}s:1:"Q";a:4:{s:1:"U";a:1:{s:1:"O";a:1:{s:1:"T";a:2:{s:1:";";a:1:{s:9:"codepoint";i:34;}s:9:"codepoint";i:34;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120084;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8474;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119980;}}}}}s:1:"R";a:12:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10512;}}}}}s:1:"E";a:1:{s:1:"G";a:2:{s:1:";";a:1:{s:9:"codepoint";i:174;}s:9:"codepoint";i:174;}}s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:340;}}}}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10219;}}}s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8608;}s:1:"t";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10518;}}}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:344;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:342;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1056;}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8476;}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:2:{s:1:"E";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8715;}}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8651;}}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10607;}}}}}}}}}}}}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8476;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:929;}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:8:{s:1:"A";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10217;}}}}}}}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8594;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8677;}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8644;}}}}}}}}}}}}}}}s:1:"C";a:1:{s:1:"e";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8969;}}}}}}}}s:1:"D";a:1:{s:1:"o";a:2:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"B";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10215;}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:2:{s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10589;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8642;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10581;}}}}}}}}}}}}}}s:1:"F";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8971;}}}}}}s:1:"T";a:2:{s:1:"e";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8866;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8614;}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10587;}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8883;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10704;}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8885;}}}}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:3:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10575;}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10588;}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8638;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10580;}}}}}}}}}}}}s:1:"V";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8640;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10579;}}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}}}}}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8477;}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:"I";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10608;}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8667;}}}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8475;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8625;}}}s:1:"u";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"D";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"y";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10740;}}}}}}}}}}}}s:1:"S";a:13:{s:1:"H";a:2:{s:1:"C";a:1:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1065;}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1064;}}}}s:1:"O";a:1:{s:1:"F";a:1:{s:1:"T";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1068;}}}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:346;}}}}}}s:1:"c";a:5:{s:1:";";a:1:{s:9:"codepoint";i:10940;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:352;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:350;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:348;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1057;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120086;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:4:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8595;}}}}}}}}}}s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8592;}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8594;}}}}}}}}}}}s:1:"U";a:1:{s:1:"p";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8593;}}}}}}}}}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:931;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"C";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8728;}}}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120138;}}}}s:1:"q";a:2:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8730;}}}s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:9633;}s:1:"I";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8851;}}}}}}}}}}}}}s:1:"S";a:1:{s:1:"u";a:2:{s:1:"b";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8847;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8849;}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8848;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8850;}}}}}}}}}}}}}}s:1:"U";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8852;}}}}}}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119982;}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8902;}}}}s:1:"u";a:4:{s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8912;}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8912;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8838;}}}}}}}}}}s:1:"c";a:2:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8827;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10928;}}}}}}s:1:"S";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8829;}}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8831;}}}}}}}}}}}s:1:"h";a:1:{s:1:"T";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8715;}}}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8721;}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8913;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8835;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8839;}}}}}}}}}}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8913;}}}}}}}s:1:"T";a:11:{s:1:"H";a:1:{s:1:"O";a:1:{s:1:"R";a:1:{s:1:"N";a:2:{s:1:";";a:1:{s:9:"codepoint";i:222;}s:9:"codepoint";i:222;}}}}s:1:"R";a:1:{s:1:"A";a:1:{s:1:"D";a:1:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8482;}}}}}s:1:"S";a:2:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1035;}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1062;}}}}s:1:"a";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:932;}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:356;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:354;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1058;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120087;}}}s:1:"h";a:2:{s:1:"e";a:2:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8756;}}}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:920;}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8201;}}}}}}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8764;}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8771;}}}}}}s:1:"F";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8773;}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8776;}}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120139;}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8411;}}}}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119983;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:358;}}}}}}}s:1:"U";a:14:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:218;}s:9:"codepoint";i:218;}}}}s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8607;}s:1:"o";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10569;}}}}}}}}s:1:"b";a:1:{s:1:"r";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1038;}}}s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:364;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:219;}s:9:"codepoint";i:219;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1059;}}}s:1:"d";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:368;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120088;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:217;}s:9:"codepoint";i:217;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:362;}}}}}s:1:"n";a:2:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"B";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:818;}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9183;}}s:1:"k";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9141;}}}}}}}}s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9181;}}}}}}}}}}}}}}}s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8899;}s:1:"P";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8846;}}}}}}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:370;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120140;}}}}s:1:"p";a:8:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8593;}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10514;}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8645;}}}}}}}}}}}}}}}s:1:"D";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8597;}}}}}}}}}}s:1:"E";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10606;}}}}}}}}}}}}s:1:"T";a:1:{s:1:"e";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8869;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8613;}}}}}}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8657;}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8661;}}}}}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"r";a:2:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8598;}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8599;}}}}}}}}}}}}}}s:1:"s";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:978;}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:933;}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:366;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119984;}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:360;}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:220;}s:9:"codepoint";i:220;}}}}s:1:"V";a:9:{s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8875;}}}}}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10987;}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1042;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8873;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10982;}}}}}}s:1:"e";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8897;}}s:1:"r";a:3:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8214;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8214;}s:1:"i";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:4:{s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8739;}}}}s:1:"L";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:124;}}}}}s:1:"S";a:1:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10072;}}}}}}}}}}s:1:"T";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8768;}}}}}}}}}}}s:1:"y";a:1:{s:1:"T";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8202;}}}}}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120089;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120141;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119985;}}}}s:1:"v";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8874;}}}}}}}s:1:"W";a:5:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:372;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8896;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120090;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120142;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119986;}}}}}s:1:"X";a:4:{s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120091;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:926;}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120143;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119987;}}}}}s:1:"Y";a:9:{s:1:"A";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1071;}}}}s:1:"I";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1031;}}}}s:1:"U";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1070;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:221;}s:9:"codepoint";i:221;}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:374;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1067;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120092;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120144;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119988;}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:376;}}}}}s:1:"Z";a:8:{s:1:"H";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1046;}}}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:377;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:381;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1047;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:379;}}}}s:1:"e";a:2:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"W";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"S";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8203;}}}}}}}}}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:918;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8488;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8484;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119989;}}}}}s:1:"a";a:16:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:225;}s:9:"codepoint";i:225;}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:259;}}}}}}s:1:"c";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8766;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8767;}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:226;}s:9:"codepoint";i:226;}}}s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:180;}s:9:"codepoint";i:180;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1072;}}}s:1:"e";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:230;}s:9:"codepoint";i:230;}}}}s:1:"f";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8289;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120094;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:224;}s:9:"codepoint";i:224;}}}}}s:1:"l";a:2:{s:1:"e";a:2:{s:1:"f";a:1:{s:1:"s";a:1:{s:1:"y";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8501;}}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8501;}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:945;}}}}}s:1:"m";a:2:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:257;}}}s:1:"l";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10815;}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:38;}s:9:"codepoint";i:38;}}s:1:"n";a:2:{s:1:"d";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8743;}s:1:"a";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10837;}}}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10844;}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10840;}}}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10842;}}}s:1:"g";a:7:{s:1:";";a:1:{s:9:"codepoint";i:8736;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10660;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8736;}}}s:1:"m";a:1:{s:1:"s";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8737;}s:1:"a";a:8:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10664;}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10665;}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10666;}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10667;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10668;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10669;}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10670;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10671;}}}}}}s:1:"r";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8735;}s:1:"v";a:1:{s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8894;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10653;}}}}}}s:1:"s";a:2:{s:1:"p";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8738;}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8491;}}}s:1:"z";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9084;}}}}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:261;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120146;}}}}s:1:"p";a:7:{s:1:";";a:1:{s:9:"codepoint";i:8776;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10864;}}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10863;}}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8778;}}s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8779;}}}s:1:"o";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:39;}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8776;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8778;}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:229;}s:9:"codepoint";i:229;}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119990;}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:42;}}s:1:"y";a:1:{s:1:"m";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8776;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8781;}}}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:227;}s:9:"codepoint";i:227;}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:228;}s:9:"codepoint";i:228;}}}s:1:"w";a:2:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8755;}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10769;}}}}}}s:1:"b";a:16:{s:1:"N";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10989;}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"k";a:4:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8780;}}}}}s:1:"e";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1014;}}}}}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8245;}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8765;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8909;}}}}}}}}s:1:"r";a:2:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8893;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8965;}s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8965;}}}}}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9141;}s:1:"t";a:1:{s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9142;}}}}}}}}s:1:"c";a:2:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8780;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1073;}}}s:1:"d";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8222;}}}}}s:1:"e";a:5:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"u";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8757;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8757;}}}}}}s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10672;}}}}}}s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1014;}}}}s:1:"r";a:1:{s:1:"n";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8492;}}}}}s:1:"t";a:3:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:946;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8502;}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8812;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120095;}}}s:1:"i";a:1:{s:1:"g";a:7:{s:1:"c";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8898;}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9711;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8899;}}}}s:1:"o";a:3:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10752;}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10753;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10754;}}}}}}}s:1:"s";a:2:{s:1:"q";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10758;}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9733;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9661;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9651;}}}}}}}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10756;}}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8897;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8896;}}}}}}}}s:1:"k";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10509;}}}}}}s:1:"l";a:3:{s:1:"a";a:2:{s:1:"c";a:1:{s:1:"k";a:3:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"z";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10731;}}}}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:9652;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9662;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9666;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9656;}}}}}}}}}}}}}}}}s:1:"n";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9251;}}}}s:1:"k";a:2:{i:1;a:2:{i:2;a:1:{s:1:";";a:1:{s:9:"codepoint";i:9618;}}i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:9617;}}}i:3;a:1:{i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:9619;}}}}s:1:"o";a:1:{s:1:"c";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9608;}}}}}s:1:"n";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8976;}}}}s:1:"o";a:4:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120147;}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8869;}s:1:"t";a:1:{s:1:"o";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8869;}}}}}s:1:"w";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8904;}}}}}s:1:"x";a:12:{s:1:"D";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9559;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9556;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9558;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9555;}}}s:1:"H";a:5:{s:1:";";a:1:{s:9:"codepoint";i:9552;}s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9574;}}s:1:"U";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9577;}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9572;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9575;}}}s:1:"U";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9565;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9562;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9564;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9561;}}}s:1:"V";a:7:{s:1:";";a:1:{s:9:"codepoint";i:9553;}s:1:"H";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9580;}}s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9571;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9568;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9579;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9570;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9567;}}}s:1:"b";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10697;}}}}s:1:"d";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9557;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9554;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9488;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9484;}}}s:1:"h";a:5:{s:1:";";a:1:{s:9:"codepoint";i:9472;}s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9573;}}s:1:"U";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9576;}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9516;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9524;}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8863;}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8862;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8864;}}}}}}s:1:"u";a:4:{s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9563;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9560;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9496;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9492;}}}s:1:"v";a:7:{s:1:";";a:1:{s:9:"codepoint";i:9474;}s:1:"H";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9578;}}s:1:"L";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9569;}}s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9566;}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9532;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9508;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9500;}}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8245;}}}}}}s:1:"r";a:2:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:728;}}}}s:1:"v";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:166;}s:9:"codepoint";i:166;}}}}}s:1:"s";a:4:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119991;}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8271;}}}}s:1:"i";a:1:{s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8765;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8909;}}}}s:1:"o";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:92;}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10693;}}}}}s:1:"u";a:2:{s:1:"l";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8226;}s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8226;}}}}}s:1:"m";a:1:{s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8782;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10926;}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8783;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8783;}}}}}}}s:1:"c";a:15:{s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:263;}}}}}s:1:"p";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8745;}s:1:"a";a:1:{s:1:"n";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10820;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10825;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10827;}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10823;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10816;}}}}}s:1:"r";a:2:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8257;}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:711;}}}}}s:1:"c";a:4:{s:1:"a";a:2:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10829;}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:269;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:231;}s:9:"codepoint";i:231;}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:265;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10828;}s:1:"s";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10832;}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:267;}}}}s:1:"e";a:3:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:184;}s:9:"codepoint";i:184;}}}s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10674;}}}}}}s:1:"n";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:162;}s:9:"codepoint";i:162;s:1:"e";a:1:{s:1:"r";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:183;}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120096;}}}s:1:"h";a:3:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1095;}}}s:1:"e";a:1:{s:1:"c";a:1:{s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10003;}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10003;}}}}}}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:967;}}}s:1:"i";a:1:{s:1:"r";a:7:{s:1:";";a:1:{s:9:"codepoint";i:9675;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10691;}}s:1:"c";a:3:{s:1:";";a:1:{s:9:"codepoint";i:710;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8791;}}}s:1:"l";a:1:{s:1:"e";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8634;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8635;}}}}}}}}}}}s:1:"d";a:5:{s:1:"R";a:1:{s:1:";";a:1:{s:9:"codepoint";i:174;}}s:1:"S";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9416;}}s:1:"a";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8859;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8858;}}}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8861;}}}}}}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8791;}}s:1:"f";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10768;}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10991;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10690;}}}}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9827;}s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9827;}}}}}}}}s:1:"o";a:4:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:58;}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8788;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8788;}}}}}}s:1:"m";a:2:{s:1:"m";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:44;}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64;}}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8705;}s:1:"f";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8728;}}}s:1:"l";a:1:{s:1:"e";a:2:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8705;}}}}}s:1:"x";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8450;}}}}}}}}s:1:"n";a:2:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8773;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10861;}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8750;}}}}}s:1:"p";a:3:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120148;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8720;}}}}s:1:"y";a:3:{s:1:";";a:1:{s:9:"codepoint";i:169;}s:9:"codepoint";i:169;s:1:"s";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8471;}}}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8629;}}}}s:1:"o";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10007;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119992;}}}s:1:"u";a:2:{s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10959;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10961;}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10960;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10962;}}}}}s:1:"t";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8943;}}}}}s:1:"u";a:7:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:2:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10552;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10549;}}}}}}s:1:"e";a:2:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8926;}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8927;}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8630;}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10557;}}}}}}s:1:"p";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8746;}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10824;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10822;}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10826;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8845;}}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10821;}}}}s:1:"r";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8631;}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10556;}}}}}s:1:"l";a:1:{s:1:"y";a:3:{s:1:"e";a:1:{s:1:"q";a:2:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8926;}}}}}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8927;}}}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8910;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8911;}}}}}}}}s:1:"r";a:1:{s:1:"e";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:164;}s:9:"codepoint";i:164;}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8630;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8631;}}}}}}}}}}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8910;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8911;}}}}}s:1:"w";a:2:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8754;}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8753;}}}}}s:1:"y";a:1:{s:1:"l";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9005;}}}}}}}s:1:"d";a:19:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8659;}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10597;}}}}s:1:"a";a:4:{s:1:"g";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8224;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8504;}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8595;}}}s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8208;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8867;}}}}}s:1:"b";a:2:{s:1:"k";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10511;}}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:733;}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:271;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1076;}}}s:1:"d";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8518;}s:1:"a";a:2:{s:1:"g";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8225;}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8650;}}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10871;}}}}}}}s:1:"e";a:3:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:176;}s:9:"codepoint";i:176;}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:948;}}}}s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10673;}}}}}}}s:1:"f";a:2:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10623;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120097;}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8643;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8642;}}}}}s:1:"i";a:5:{s:1:"a";a:1:{s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8900;}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8900;}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9830;}}}}}}}}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9830;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:168;}}s:1:"g";a:1:{s:1:"a";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:989;}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8946;}}}}s:1:"v";a:3:{s:1:";";a:1:{s:9:"codepoint";i:247;}s:1:"i";a:1:{s:1:"d";a:1:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:247;}s:9:"codepoint";i:247;s:1:"o";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8903;}}}}}}}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8903;}}}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1106;}}}}s:1:"l";a:1:{s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8990;}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8973;}}}}}}s:1:"o";a:5:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:36;}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120149;}}}s:1:"t";a:5:{s:1:";";a:1:{s:9:"codepoint";i:729;}s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8784;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8785;}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8760;}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8724;}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8865;}}}}}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8966;}}}}}}}}}}}}}s:1:"w";a:1:{s:1:"n";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8595;}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8650;}}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8643;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8642;}}}}}}}}}}}}}}}}s:1:"r";a:2:{s:1:"b";a:1:{s:1:"k";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10512;}}}}}}}s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8991;}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8972;}}}}}}s:1:"s";a:3:{s:1:"c";a:2:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119993;}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1109;}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10742;}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:273;}}}}}}s:1:"t";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8945;}}}}s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9663;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9662;}}}}}s:1:"u";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8693;}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10607;}}}}}s:1:"w";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10662;}}}}}}}s:1:"z";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1119;}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10239;}}}}}}}}}s:1:"e";a:18:{s:1:"D";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10871;}}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8785;}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:233;}s:9:"codepoint";i:233;}}}}s:1:"s";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10862;}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:283;}}}}}s:1:"i";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8790;}s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:234;}s:9:"codepoint";i:234;}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8789;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1101;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:279;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8519;}}s:1:"f";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8786;}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120098;}}}s:1:"g";a:3:{s:1:";";a:1:{s:9:"codepoint";i:10906;}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:232;}s:9:"codepoint";i:232;}}}}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10902;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10904;}}}}}}s:1:"l";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10905;}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9191;}}}}}}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8467;}}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10901;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10903;}}}}}}s:1:"m";a:3:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:275;}}}}s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8709;}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8709;}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8709;}}}}}s:1:"s";a:1:{s:1:"p";a:2:{i:1;a:2:{i:3;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8196;}}i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8197;}}}s:1:";";a:1:{s:9:"codepoint";i:8195;}}}}s:1:"n";a:2:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:331;}}s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8194;}}}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:281;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120150;}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8917;}s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10723;}}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10865;}}}}s:1:"s";a:1:{s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:1013;}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:949;}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:949;}}}}}s:1:"q";a:4:{s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8790;}}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8789;}}}}}}s:1:"s";a:2:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8770;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:2:{s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10902;}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10901;}}}}}}}}}}s:1:"u";a:3:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:61;}}}}s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8799;}}}}s:1:"i";a:1:{s:1:"v";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8801;}s:1:"D";a:1:{s:1:"D";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10872;}}}}}}s:1:"v";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10725;}}}}}}}}s:1:"r";a:2:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8787;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10609;}}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8495;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8784;}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8770;}}}}s:1:"t";a:2:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:951;}}s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:240;}s:9:"codepoint";i:240;}}s:1:"u";a:2:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:235;}s:9:"codepoint";i:235;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8364;}}}}s:1:"x";a:3:{s:1:"c";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:33;}}}s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8707;}}}}s:1:"p";a:2:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8496;}}}}}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8519;}}}}}}}}}}}}}s:1:"f";a:11:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8786;}}}}}}}}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1092;}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9792;}}}}}}s:1:"f";a:3:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64259;}}}}}s:1:"l";a:2:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64256;}}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64260;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120099;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64257;}}}}}s:1:"l";a:3:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9837;}}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:64258;}}}}s:1:"t";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9649;}}}}}s:1:"n";a:1:{s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:402;}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120151;}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8704;}}}}s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8916;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10969;}}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10765;}}}}}}}}s:1:"r";a:2:{s:1:"a";a:2:{s:1:"c";a:6:{i:1;a:6:{i:2;a:2:{s:1:";";a:1:{s:9:"codepoint";i:189;}s:9:"codepoint";i:189;}i:3;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8531;}}i:4;a:2:{s:1:";";a:1:{s:9:"codepoint";i:188;}s:9:"codepoint";i:188;}i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8533;}}i:6;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8537;}}i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8539;}}}i:2;a:2:{i:3;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8532;}}i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8534;}}}i:3;a:3:{i:4;a:2:{s:1:";";a:1:{s:9:"codepoint";i:190;}s:9:"codepoint";i:190;}i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8535;}}i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8540;}}}i:4;a:1:{i:5;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8536;}}}i:5;a:2:{i:6;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8538;}}i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8541;}}}i:7;a:1:{i:8;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8542;}}}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8260;}}}}s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8994;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119995;}}}}}s:1:"g";a:16:{s:1:"E";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8807;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10892;}}}s:1:"a";a:3:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:501;}}}}}s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:947;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:989;}}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10886;}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:287;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:285;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1075;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:289;}}}}s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8805;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8923;}}s:1:"q";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8805;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8807;}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10878;}}}}}}}s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10878;}s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10921;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10880;}s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10882;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10884;}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10900;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120100;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8811;}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8921;}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8503;}}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1107;}}}}s:1:"l";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8823;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10898;}}s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10917;}}s:1:"j";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10916;}}}s:1:"n";a:4:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8809;}}s:1:"a";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10890;}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10890;}}}}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10888;}s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10888;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8809;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8935;}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120152;}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:96;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8458;}}}s:1:"i";a:1:{s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8819;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10894;}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10896;}}}}}s:1:"t";a:7:{s:1:";";a:1:{s:9:"codepoint";i:62;}s:9:"codepoint";i:62;s:1:"c";a:2:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10919;}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10874;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8919;}}}}s:1:"l";a:1:{s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10645;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10876;}}}}}}s:1:"r";a:5:{s:1:"a";a:2:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10886;}}}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10616;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8919;}}}}s:1:"e";a:1:{s:1:"q";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8923;}}}}}s:1:"q";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10892;}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8823;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8819;}}}}}}}s:1:"h";a:10:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}}}s:1:"a";a:4:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8202;}}}}}s:1:"l";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:189;}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8459;}}}}}s:1:"r";a:2:{s:1:"d";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1098;}}}}s:1:"r";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8596;}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10568;}}}}s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8621;}}}}}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8463;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:293;}}}}}s:1:"e";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9829;}s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9829;}}}}}}}}s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8230;}}}}}s:1:"r";a:1:{s:1:"c";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8889;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120101;}}}s:1:"k";a:1:{s:1:"s";a:2:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10533;}}}}}}s:1:"w";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10534;}}}}}}}}s:1:"o";a:5:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8703;}}}}s:1:"m";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8763;}}}}}s:1:"o";a:1:{s:1:"k";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8617;}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8618;}}}}}}}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120153;}}}s:1:"r";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8213;}}}}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119997;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8463;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:295;}}}}}}s:1:"y";a:2:{s:1:"b";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8259;}}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8208;}}}}}}}s:1:"i";a:15:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:237;}s:9:"codepoint";i:237;}}}}}s:1:"c";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8291;}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:238;}s:9:"codepoint";i:238;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1080;}}}s:1:"e";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1077;}}}s:1:"x";a:1:{s:1:"c";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:161;}s:9:"codepoint";i:161;}}}}s:1:"f";a:2:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8660;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120102;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:236;}s:9:"codepoint";i:236;}}}}}s:1:"i";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8520;}s:1:"i";a:2:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10764;}}}}s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8749;}}}}s:1:"n";a:1:{s:1:"f";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10716;}}}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8489;}}}}}s:1:"j";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:307;}}}}}s:1:"m";a:3:{s:1:"a";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:299;}}}s:1:"g";a:3:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8465;}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8464;}}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8465;}}}}}}s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:305;}}}}s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8887;}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:437;}}}}}s:1:"n";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8712;}s:1:"c";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8453;}}}}}s:1:"f";a:1:{s:1:"i";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8734;}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10717;}}}}}}}s:1:"o";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:305;}}}}}s:1:"t";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8747;}s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8890;}}}}s:1:"e";a:2:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8484;}}}}}s:1:"r";a:1:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8890;}}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10775;}}}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10812;}}}}}}}s:1:"o";a:4:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1105;}}}s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:303;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120154;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:953;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10812;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:191;}s:9:"codepoint";i:191;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119998;}}}s:1:"i";a:1:{s:1:"n";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8712;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8953;}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8949;}}}}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8948;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8947;}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8712;}}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8290;}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:297;}}}}}}s:1:"u";a:2:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1110;}}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:239;}s:9:"codepoint";i:239;}}}}s:1:"j";a:6:{s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:309;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1081;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120103;}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:567;}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120155;}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:119999;}}}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1112;}}}}}}s:1:"u";a:1:{s:1:"k";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1108;}}}}}}s:1:"k";a:8:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"a";a:2:{s:1:";";a:1:{s:9:"codepoint";i:954;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1008;}}}}}}s:1:"c";a:2:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:311;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1082;}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120104;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:312;}}}}}}s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1093;}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1116;}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120156;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120000;}}}}}s:1:"l";a:22:{s:1:"A";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8666;}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8656;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10523;}}}}}}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10510;}}}}}s:1:"E";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8806;}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10891;}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10594;}}}}s:1:"a";a:9:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:314;}}}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10676;}}}}}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8466;}}}}}s:1:"m";a:1:{s:1:"b";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:955;}}}}}s:1:"n";a:1:{s:1:"g";a:3:{s:1:";";a:1:{s:9:"codepoint";i:10216;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10641;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10216;}}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10885;}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:171;}s:9:"codepoint";i:171;}}}s:1:"r";a:1:{s:1:"r";a:8:{s:1:";";a:1:{s:9:"codepoint";i:8592;}s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8676;}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10527;}}}}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10525;}}}s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8617;}}}s:1:"l";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8619;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10553;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10611;}}}}s:1:"t";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8610;}}}}}s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:10923;}s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10521;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10925;}}}}s:1:"b";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10508;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10098;}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:123;}}s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:91;}}}}s:1:"k";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10635;}}s:1:"s";a:1:{s:1:"l";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10639;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10637;}}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:318;}}}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:316;}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8968;}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:123;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1083;}}}s:1:"d";a:4:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10550;}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8220;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8222;}}}}}s:1:"r";a:2:{s:1:"d";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10599;}}}}}s:1:"u";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10571;}}}}}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8626;}}}}s:1:"e";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8804;}s:1:"f";a:1:{s:1:"t";a:5:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8592;}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8610;}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8637;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8636;}}}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8647;}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8596;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8646;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8651;}}}}}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8621;}}}}}}}}}}}}}}}}s:1:"t";a:1:{s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8907;}}}}}}}}}}}}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8922;}}s:1:"q";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8804;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8806;}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10877;}}}}}}}s:1:"s";a:5:{s:1:";";a:1:{s:9:"codepoint";i:10877;}s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10920;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10879;}s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10881;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10883;}}}}}}s:1:"g";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10899;}}}}s:1:"s";a:5:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10885;}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8918;}}}}s:1:"e";a:1:{s:1:"q";a:2:{s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8922;}}}}s:1:"q";a:1:{s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10891;}}}}}}}s:1:"g";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8822;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8818;}}}}}}}s:1:"f";a:3:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10620;}}}}}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8970;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120105;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8822;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10897;}}}s:1:"h";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8637;}}s:1:"u";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8636;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10602;}}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9604;}}}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1113;}}}}s:1:"l";a:5:{s:1:";";a:1:{s:9:"codepoint";i:8810;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8647;}}}}s:1:"c";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8990;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10603;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9722;}}}}}s:1:"m";a:2:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:320;}}}}}s:1:"o";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9136;}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9136;}}}}}}}}}}s:1:"n";a:4:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8808;}}s:1:"a";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10889;}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10889;}}}}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10887;}s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10887;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8808;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8934;}}}}}s:1:"o";a:8:{s:1:"a";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10220;}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8701;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10214;}}}}s:1:"n";a:1:{s:1:"g";a:3:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10229;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10231;}}}}}}}}}}}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10236;}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10230;}}}}}}}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8619;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8620;}}}}}}}}}}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10629;}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120157;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10797;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10804;}}}}}}s:1:"w";a:2:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8727;}}}}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:95;}}}}}s:1:"z";a:3:{s:1:";";a:1:{s:9:"codepoint";i:9674;}s:1:"e";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9674;}}}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10731;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:40;}s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10643;}}}}}}s:1:"r";a:5:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8646;}}}}s:1:"c";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8991;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8651;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10605;}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8206;}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8895;}}}}}s:1:"s";a:6:{s:1:"a";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8249;}}}}}s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120001;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8624;}}s:1:"i";a:1:{s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8818;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10893;}}s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10895;}}}}s:1:"q";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:91;}}s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8216;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8218;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:322;}}}}}}s:1:"t";a:9:{s:1:";";a:1:{s:9:"codepoint";i:60;}s:9:"codepoint";i:60;s:1:"c";a:2:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10918;}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10873;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8918;}}}}s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8907;}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8905;}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10614;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10875;}}}}}}s:1:"r";a:2:{s:1:"P";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10646;}}}}s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:9667;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8884;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9666;}}}}}s:1:"u";a:1:{s:1:"r";a:2:{s:1:"d";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10570;}}}}}}s:1:"u";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10598;}}}}}}}}s:1:"m";a:14:{s:1:"D";a:1:{s:1:"D";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8762;}}}}}s:1:"a";a:4:{s:1:"c";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:175;}s:9:"codepoint";i:175;}}s:1:"l";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9794;}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10016;}s:1:"e";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10016;}}}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8614;}s:1:"s";a:1:{s:1:"t";a:1:{s:1:"o";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8614;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8615;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8612;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8613;}}}}}}}s:1:"r";a:1:{s:1:"k";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9646;}}}}}}s:1:"c";a:2:{s:1:"o";a:1:{s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10793;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1084;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8212;}}}}}s:1:"e";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8737;}}}}}}}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120106;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8487;}}}s:1:"i";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:181;}s:9:"codepoint";i:181;}}}s:1:"d";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8739;}s:1:"a";a:1:{s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:42;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10992;}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:183;}s:9:"codepoint";i:183;}}}}s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8722;}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8863;}}s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8760;}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10794;}}}}}}}s:1:"l";a:2:{s:1:"c";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10971;}}}s:1:"d";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8230;}}}}s:1:"n";a:1:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8723;}}}}}}s:1:"o";a:2:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8871;}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120158;}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8723;}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120002;}}}s:1:"t";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8766;}}}}}}s:1:"u";a:3:{s:1:";";a:1:{s:9:"codepoint";i:956;}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8888;}}}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8888;}}}}}}s:1:"n";a:23:{s:1:"L";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8653;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8654;}}}}}}}}}}}}}}}s:1:"R";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8655;}}}}}}}}}}}s:1:"V";a:2:{s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8879;}}}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8878;}}}}}}s:1:"a";a:4:{s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8711;}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:324;}}}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8777;}s:1:"o";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:329;}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8777;}}}}}}s:1:"t";a:1:{s:1:"u";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9838;}s:1:"a";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9838;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8469;}}}}}}}}s:1:"b";a:1:{s:1:"s";a:1:{s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:160;}s:9:"codepoint";i:160;}}}s:1:"c";a:5:{s:1:"a";a:2:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10819;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:328;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:326;}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8775;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10818;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1085;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8211;}}}}}s:1:"e";a:6:{s:1:";";a:1:{s:9:"codepoint";i:8800;}s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8663;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10532;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8599;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8599;}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8802;}}}}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10536;}}}}}s:1:"x";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8708;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8708;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120107;}}}s:1:"g";a:3:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8817;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8817;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8821;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8815;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8815;}}}}s:1:"h";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8654;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8622;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10994;}}}}}s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8715;}s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8956;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8954;}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8715;}}}s:1:"j";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1114;}}}}s:1:"l";a:6:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8653;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8602;}}}}s:1:"d";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8229;}}}s:1:"e";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8816;}s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8602;}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8622;}}}}}}}}}}}}}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8816;}}s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8814;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8820;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8814;}s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8938;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8940;}}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}s:1:"o";a:2:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120159;}}}s:1:"t";a:4:{s:1:";";a:1:{s:9:"codepoint";i:172;}s:9:"codepoint";i:172;s:1:"i";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8713;}s:1:"v";a:3:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8713;}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8951;}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8950;}}}}}s:1:"n";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8716;}s:1:"v";a:3:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8716;}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8958;}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8957;}}}}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8742;}s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}}}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10772;}}}}}}s:1:"r";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8832;}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8928;}}}}s:1:"e";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8832;}}}}}s:1:"r";a:4:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8655;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8603;}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8603;}}}}}}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8939;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8941;}}}}}}s:1:"s";a:7:{s:1:"c";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8833;}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8929;}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120003;}}}s:1:"h";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:2:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}}}}}}}}}}s:1:"i";a:1:{s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8769;}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8772;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8772;}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8740;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8742;}}}}s:1:"q";a:1:{s:1:"s";a:1:{s:1:"u";a:2:{s:1:"b";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8930;}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8931;}}}}}}s:1:"u";a:3:{s:1:"b";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8836;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8840;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8840;}}}}}}}s:1:"c";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8833;}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8837;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8841;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8841;}}}}}}}}}s:1:"t";a:4:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8825;}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:241;}s:9:"codepoint";i:241;}}}}s:1:"l";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8824;}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8938;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8940;}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8939;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8941;}}}}}}}}}}}}}}}}s:1:"u";a:2:{s:1:";";a:1:{s:9:"codepoint";i:957;}s:1:"m";a:3:{s:1:";";a:1:{s:9:"codepoint";i:35;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8470;}}}}s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8199;}}}}}s:1:"v";a:6:{s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8877;}}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10500;}}}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8876;}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"f";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10718;}}}}}}s:1:"l";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10498;}}}}}s:1:"r";a:1:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10499;}}}}}}s:1:"w";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8662;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10531;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8598;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8598;}}}}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10535;}}}}}}}s:1:"o";a:18:{s:1:"S";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9416;}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:243;}s:9:"codepoint";i:243;}}}}s:1:"s";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8859;}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8858;}s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:244;}s:9:"codepoint";i:244;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1086;}}}s:1:"d";a:5:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8861;}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:337;}}}}}s:1:"i";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10808;}}}s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8857;}}}s:1:"s";a:1:{s:1:"o";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10684;}}}}}}s:1:"e";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:339;}}}}}s:1:"f";a:2:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10687;}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120108;}}}s:1:"g";a:3:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:731;}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:242;}s:9:"codepoint";i:242;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10689;}}}s:1:"h";a:2:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10677;}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8486;}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8750;}}}}s:1:"l";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8634;}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10686;}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"s";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10683;}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8254;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10688;}}}s:1:"m";a:3:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:333;}}}}s:1:"e";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:969;}}}}s:1:"i";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:959;}}}}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10678;}}s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8854;}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120160;}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10679;}}}s:1:"e";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10681;}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8853;}}}}}s:1:"r";a:7:{s:1:";";a:1:{s:9:"codepoint";i:8744;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8635;}}}}s:1:"d";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10845;}s:1:"e";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8500;}s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8500;}}}}}s:1:"f";a:2:{s:1:";";a:1:{s:9:"codepoint";i:170;}s:9:"codepoint";i:170;}s:1:"m";a:2:{s:1:";";a:1:{s:9:"codepoint";i:186;}s:9:"codepoint";i:186;}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8886;}}}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10838;}}}s:1:"s";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10839;}}}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10843;}}}s:1:"s";a:3:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8500;}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:2:{s:1:";";a:1:{s:9:"codepoint";i:248;}s:9:"codepoint";i:248;}}}}s:1:"o";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8856;}}}}s:1:"t";a:1:{s:1:"i";a:2:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:245;}s:9:"codepoint";i:245;}}}s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8855;}s:1:"a";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10806;}}}}}}}}s:1:"u";a:1:{s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:246;}s:9:"codepoint";i:246;}}}s:1:"v";a:1:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9021;}}}}}}s:1:"p";a:12:{s:1:"a";a:1:{s:1:"r";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8741;}s:1:"a";a:3:{s:1:";";a:1:{s:9:"codepoint";i:182;}s:9:"codepoint";i:182;s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}}}s:1:"s";a:2:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10995;}}}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:11005;}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8706;}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1087;}}}s:1:"e";a:1:{s:1:"r";a:5:{s:1:"c";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:37;}}}}s:1:"i";a:1:{s:1:"o";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:46;}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8240;}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8869;}}s:1:"t";a:1:{s:1:"e";a:1:{s:1:"n";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8241;}}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120109;}}}s:1:"h";a:3:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:966;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:966;}}}s:1:"m";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8499;}}}}}s:1:"o";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9742;}}}}}s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:960;}s:1:"t";a:1:{s:1:"c";a:1:{s:1:"h";a:1:{s:1:"f";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8916;}}}}}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:982;}}}s:1:"l";a:2:{s:1:"a";a:1:{s:1:"n";a:2:{s:1:"c";a:1:{s:1:"k";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8463;}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8462;}}}}s:1:"k";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8463;}}}}}s:1:"u";a:1:{s:1:"s";a:9:{s:1:";";a:1:{s:9:"codepoint";i:43;}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10787;}}}}}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8862;}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10786;}}}}s:1:"d";a:2:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8724;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10789;}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10866;}}s:1:"m";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:177;}s:9:"codepoint";i:177;}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10790;}}}}s:1:"t";a:1:{s:1:"w";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10791;}}}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:177;}}s:1:"o";a:3:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10773;}}}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120161;}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"d";a:2:{s:1:";";a:1:{s:9:"codepoint";i:163;}s:9:"codepoint";i:163;}}}}s:1:"r";a:10:{s:1:";";a:1:{s:9:"codepoint";i:8826;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10931;}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10935;}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8828;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10927;}s:1:"c";a:6:{s:1:";";a:1:{s:9:"codepoint";i:8826;}s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10935;}}}}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8828;}}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10927;}}}s:1:"n";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10937;}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10933;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8936;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8830;}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8242;}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8473;}}}}}s:1:"n";a:3:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10933;}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10937;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8936;}}}}}s:1:"o";a:3:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8719;}}s:1:"f";a:3:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9006;}}}}}s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8978;}}}}}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8979;}}}}}}s:1:"p";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8733;}s:1:"t";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8830;}}}}s:1:"u";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8880;}}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120005;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:968;}}}s:1:"u";a:1:{s:1:"n";a:1:{s:1:"c";a:1:{s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8200;}}}}}}}s:1:"q";a:6:{s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120110;}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10764;}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120162;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8279;}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120006;}}}}s:1:"u";a:3:{s:1:"a";a:1:{s:1:"t";a:2:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"n";a:1:{s:1:"i";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8461;}}}}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10774;}}}}}}s:1:"e";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:63;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8799;}}}}}}s:1:"o";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:34;}s:9:"codepoint";i:34;}}}}s:1:"r";a:21:{s:1:"A";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8667;}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8658;}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10524;}}}}}}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10511;}}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10596;}}}}s:1:"a";a:7:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10714;}}s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:341;}}}}}s:1:"d";a:1:{s:1:"i";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8730;}}}}s:1:"e";a:1:{s:1:"m";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"y";a:1:{s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10675;}}}}}}}s:1:"n";a:1:{s:1:"g";a:4:{s:1:";";a:1:{s:9:"codepoint";i:10217;}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10642;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10661;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10217;}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:187;}s:9:"codepoint";i:187;}}}s:1:"r";a:1:{s:1:"r";a:11:{s:1:";";a:1:{s:9:"codepoint";i:8594;}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10613;}}}s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8677;}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10528;}}}}s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10547;}}s:1:"f";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10526;}}}s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8618;}}}s:1:"l";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8620;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10565;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10612;}}}}s:1:"t";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8611;}}}s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8605;}}}}s:1:"t";a:2:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10522;}}}}s:1:"i";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8758;}s:1:"n";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8474;}}}}}}}}}s:1:"b";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10509;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10099;}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:125;}}s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:93;}}}}s:1:"k";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10636;}}s:1:"s";a:1:{s:1:"l";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10638;}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10640;}}}}}}}s:1:"c";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:345;}}}}}s:1:"e";a:2:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:343;}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8969;}}}}s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:125;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1088;}}}s:1:"d";a:4:{s:1:"c";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10551;}}}s:1:"l";a:1:{s:1:"d";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10601;}}}}}}s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8221;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8221;}}}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8627;}}}}s:1:"e";a:3:{s:1:"a";a:1:{s:1:"l";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8476;}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8475;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8476;}}}}}s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8477;}}}}s:1:"c";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9645;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:174;}s:9:"codepoint";i:174;}}s:1:"f";a:3:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10621;}}}}}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8971;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120111;}}}s:1:"h";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8641;}}s:1:"u";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8640;}s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10604;}}}}}s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:961;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1009;}}}}s:1:"i";a:3:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:6:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8594;}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8611;}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8641;}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8640;}}}}}}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8644;}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8652;}}}}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8649;}}}}}}}}}}}}s:1:"s";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8605;}}}}}}}}}}}s:1:"t";a:1:{s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8908;}}}}}}}}}}}}}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:730;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8787;}}}}}}}}}}}}s:1:"l";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8644;}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8652;}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8207;}}}s:1:"m";a:1:{s:1:"o";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9137;}s:1:"a";a:1:{s:1:"c";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9137;}}}}}}}}}}s:1:"n";a:1:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10990;}}}}}s:1:"o";a:4:{s:1:"a";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10221;}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8702;}}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10215;}}}}s:1:"p";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10630;}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120163;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10798;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10805;}}}}}}}s:1:"p";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:41;}s:1:"g";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10644;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10770;}}}}}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8649;}}}}}s:1:"s";a:4:{s:1:"a";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8250;}}}}}s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120007;}}}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8625;}}s:1:"q";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:93;}}s:1:"u";a:1:{s:1:"o";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8217;}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8217;}}}}}}s:1:"t";a:3:{s:1:"h";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8908;}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8906;}}}}}s:1:"r";a:1:{s:1:"i";a:4:{s:1:";";a:1:{s:9:"codepoint";i:9657;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8885;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9656;}}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10702;}}}}}}}}s:1:"u";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10600;}}}}}}}s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8478;}}}s:1:"s";a:19:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:347;}}}}}}s:1:"b";a:1:{s:1:"q";a:1:{s:1:"u";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8218;}}}}}s:1:"c";a:10:{s:1:";";a:1:{s:9:"codepoint";i:8827;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10932;}}s:1:"a";a:2:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10936;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:353;}}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8829;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10928;}s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:351;}}}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:349;}}}}s:1:"n";a:3:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10934;}}s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10938;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8937;}}}}}s:1:"p";a:1:{s:1:"o";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10771;}}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8831;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1089;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8901;}s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8865;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10854;}}}}}s:1:"e";a:7:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8664;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10533;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8600;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8600;}}}}}}s:1:"c";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:167;}s:9:"codepoint";i:167;}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:59;}}}s:1:"s";a:1:{s:1:"w";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10537;}}}}}s:1:"t";a:1:{s:1:"m";a:2:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}s:1:"x";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10038;}}}}s:1:"f";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:120112;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8994;}}}}}}s:1:"h";a:4:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9839;}}}}s:1:"c";a:2:{s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1097;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1096;}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:"t";a:2:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8739;}}}}s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}}}}}}}}}s:1:"y";a:2:{s:1:";";a:1:{s:9:"codepoint";i:173;}s:9:"codepoint";i:173;}}s:1:"i";a:2:{s:1:"g";a:1:{s:1:"m";a:1:{s:1:"a";a:3:{s:1:";";a:1:{s:9:"codepoint";i:963;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:962;}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:962;}}}}}s:1:"m";a:8:{s:1:";";a:1:{s:9:"codepoint";i:8764;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10858;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8771;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8771;}}}s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10910;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10912;}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10909;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10911;}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8774;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10788;}}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10610;}}}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8592;}}}}}s:1:"m";a:4:{s:1:"a";a:2:{s:1:"l";a:1:{s:1:"l";a:1:{s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}}}}}}}s:1:"s";a:1:{s:1:"h";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10803;}}}}}s:1:"e";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"s";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10724;}}}}}}}s:1:"i";a:2:{s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8739;}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8995;}}}}s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10922;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10924;}}}}s:1:"o";a:3:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1100;}}}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:47;}s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10692;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9023;}}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120164;}}}}s:1:"p";a:1:{s:1:"a";a:2:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:"s";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9824;}s:1:"u";a:1:{s:1:"i";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9824;}}}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8741;}}}}s:1:"q";a:3:{s:1:"c";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8851;}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8852;}}}}s:1:"s";a:1:{s:1:"u";a:2:{s:1:"b";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8847;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8849;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8847;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8849;}}}}}}}s:1:"p";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8848;}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8850;}}s:1:"s";a:1:{s:1:"e";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8848;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8850;}}}}}}}}}s:1:"u";a:3:{s:1:";";a:1:{s:9:"codepoint";i:9633;}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9633;}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9642;}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8594;}}}}}s:1:"s";a:4:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120008;}}}s:1:"e";a:1:{s:1:"t";a:1:{s:1:"m";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8726;}}}}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8995;}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8902;}}}}}}s:1:"t";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9734;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9733;}}}}s:1:"r";a:2:{s:1:"a";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1013;}}}}}}}}s:1:"p";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:981;}}}}}}}}}s:1:"n";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:175;}}}}}s:1:"u";a:5:{s:1:"b";a:9:{s:1:";";a:1:{s:9:"codepoint";i:8834;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10949;}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10941;}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8838;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10947;}}}}}s:1:"m";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10945;}}}}}s:1:"n";a:2:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10955;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8842;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10943;}}}}}s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10617;}}}}}s:1:"s";a:3:{s:1:"e";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8834;}s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8838;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10949;}}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8842;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10955;}}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10951;}}}s:1:"u";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10965;}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10963;}}}}}s:1:"c";a:1:{s:1:"c";a:6:{s:1:";";a:1:{s:9:"codepoint";i:8827;}s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10936;}}}}}}}s:1:"c";a:1:{s:1:"u";a:1:{s:1:"r";a:1:{s:1:"l";a:1:{s:1:"y";a:1:{s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8829;}}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10928;}}}s:1:"n";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10938;}}}}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10934;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8937;}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8831;}}}}}}s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8721;}}s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9834;}}}s:1:"p";a:13:{i:1;a:2:{s:1:";";a:1:{s:9:"codepoint";i:185;}s:9:"codepoint";i:185;}i:2;a:2:{s:1:";";a:1:{s:9:"codepoint";i:178;}s:9:"codepoint";i:178;}i:3;a:2:{s:1:";";a:1:{s:9:"codepoint";i:179;}s:9:"codepoint";i:179;}s:1:";";a:1:{s:9:"codepoint";i:8835;}s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10950;}}s:1:"d";a:2:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10942;}}}s:1:"s";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10968;}}}}}s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8839;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10948;}}}}}s:1:"h";a:1:{s:1:"s";a:1:{s:1:"u";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10967;}}}}}s:1:"l";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10619;}}}}}s:1:"m";a:1:{s:1:"u";a:1:{s:1:"l";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10946;}}}}}s:1:"n";a:2:{s:1:"E";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10956;}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8843;}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10944;}}}}}s:1:"s";a:3:{s:1:"e";a:1:{s:1:"t";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8835;}s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8839;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10950;}}}}s:1:"n";a:1:{s:1:"e";a:1:{s:1:"q";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8843;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10956;}}}}}}}s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10952;}}}s:1:"u";a:2:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10964;}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10966;}}}}}}s:1:"w";a:3:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8665;}}}}s:1:"a";a:1:{s:1:"r";a:2:{s:1:"h";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10534;}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8601;}s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8601;}}}}}}s:1:"n";a:1:{s:1:"w";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10538;}}}}}}s:1:"z";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"g";a:2:{s:1:";";a:1:{s:9:"codepoint";i:223;}s:9:"codepoint";i:223;}}}}}s:1:"t";a:13:{s:1:"a";a:2:{s:1:"r";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8982;}}}}}s:1:"u";a:1:{s:1:";";a:1:{s:9:"codepoint";i:964;}}}s:1:"b";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9140;}}}}s:1:"c";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:357;}}}}}s:1:"e";a:1:{s:1:"d";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:355;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1090;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8411;}}}}s:1:"e";a:1:{s:1:"l";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8981;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120113;}}}s:1:"h";a:4:{s:1:"e";a:2:{s:1:"r";a:1:{s:1:"e";a:2:{i:4;a:1:{s:1:";";a:1:{s:9:"codepoint";i:8756;}}s:1:"f";a:1:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8756;}}}}}}}s:1:"t";a:1:{s:1:"a";a:3:{s:1:";";a:1:{s:9:"codepoint";i:952;}s:1:"s";a:1:{s:1:"y";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:977;}}}}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:977;}}}}}s:1:"i";a:2:{s:1:"c";a:1:{s:1:"k";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"x";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8776;}}}}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8764;}}}}}}s:1:"n";a:1:{s:1:"s";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8201;}}}}}s:1:"k";a:2:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8776;}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8764;}}}}}s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:254;}s:9:"codepoint";i:254;}}}}s:1:"i";a:3:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:732;}}}}s:1:"m";a:1:{s:1:"e";a:1:{s:1:"s";a:4:{s:1:";";a:1:{s:9:"codepoint";i:215;}s:9:"codepoint";i:215;s:1:"b";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8864;}s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10801;}}}}s:1:"d";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10800;}}}}}s:1:"n";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8749;}}}}s:1:"o";a:3:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10536;}}}s:1:"p";a:4:{s:1:";";a:1:{s:9:"codepoint";i:8868;}s:1:"b";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9014;}}}}s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10993;}}}}s:1:"f";a:2:{s:1:";";a:1:{s:9:"codepoint";i:120165;}s:1:"o";a:1:{s:1:"r";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10970;}}}}}}s:1:"s";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10537;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8244;}}}}}}s:1:"r";a:3:{s:1:"a";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8482;}}}}s:1:"i";a:7:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:5:{s:1:";";a:1:{s:9:"codepoint";i:9653;}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9663;}}}}}s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9667;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8884;}}}}}}}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8796;}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9657;}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8885;}}}}}}}}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9708;}}}}s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8796;}}s:1:"m";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10810;}}}}}}s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10809;}}}}}s:1:"s";a:1:{s:1:"b";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10701;}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10811;}}}}}}s:1:"p";a:1:{s:1:"e";a:1:{s:1:"z";a:1:{s:1:"i";a:1:{s:1:"u";a:1:{s:1:"m";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9186;}}}}}}}}s:1:"s";a:3:{s:1:"c";a:2:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120009;}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1094;}}}s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1115;}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:359;}}}}}}s:1:"w";a:2:{s:1:"i";a:1:{s:1:"x";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8812;}}}}s:1:"o";a:1:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"a";a:1:{s:1:"d";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8606;}}}}}}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8608;}}}}}}}}}}}}}}}}}}s:1:"u";a:18:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8657;}}}}s:1:"H";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10595;}}}}s:1:"a";a:2:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:250;}s:9:"codepoint";i:250;}}}}s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8593;}}}}s:1:"b";a:1:{s:1:"r";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1118;}}}s:1:"e";a:1:{s:1:"v";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:365;}}}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:2:{s:1:";";a:1:{s:9:"codepoint";i:251;}s:9:"codepoint";i:251;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1091;}}}s:1:"d";a:3:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8645;}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:369;}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10606;}}}}}s:1:"f";a:2:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10622;}}}}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120114;}}}s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"v";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:249;}s:9:"codepoint";i:249;}}}}}s:1:"h";a:2:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:"l";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8639;}}s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8638;}}}}s:1:"b";a:1:{s:1:"l";a:1:{s:1:"k";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9600;}}}}}s:1:"l";a:2:{s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8988;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8988;}}}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8975;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9720;}}}}}s:1:"m";a:2:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:363;}}}}s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:168;}s:9:"codepoint";i:168;}}s:1:"o";a:2:{s:1:"g";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:371;}}}}s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120166;}}}}s:1:"p";a:6:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8593;}}}}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"n";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8597;}}}}}}}}}}s:1:"h";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:"o";a:1:{s:1:"o";a:1:{s:1:"n";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8639;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8638;}}}}}}}}}}}}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8846;}}}}s:1:"s";a:1:{s:1:"i";a:3:{s:1:";";a:1:{s:9:"codepoint";i:965;}s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:978;}}s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:965;}}}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"w";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8648;}}}}}}}}}}s:1:"r";a:3:{s:1:"c";a:2:{s:1:"o";a:1:{s:1:"r";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8989;}s:1:"e";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8989;}}}}}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8974;}}}}}s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:367;}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9721;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120010;}}}}s:1:"t";a:3:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8944;}}}}s:1:"i";a:1:{s:1:"l";a:1:{s:1:"d";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:361;}}}}}s:1:"r";a:1:{s:1:"i";a:2:{s:1:";";a:1:{s:9:"codepoint";i:9653;}s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9652;}}}}}s:1:"u";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8648;}}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:252;}s:9:"codepoint";i:252;}}}s:1:"w";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10663;}}}}}}}}s:1:"v";a:14:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8661;}}}}s:1:"B";a:1:{s:1:"a";a:1:{s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:10984;}s:1:"v";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10985;}}}}}s:1:"D";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8872;}}}}}s:1:"a";a:2:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10652;}}}}}s:1:"r";a:7:{s:1:"e";a:1:{s:1:"p";a:1:{s:1:"s";a:1:{s:1:"i";a:1:{s:1:"l";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:949;}}}}}}}}s:1:"k";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:"p";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1008;}}}}}}s:1:"n";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8709;}}}}}}}}s:1:"p";a:3:{s:1:"h";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:966;}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:982;}}s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:"t";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8597;}s:1:"h";a:1:{s:1:"o";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1009;}}}}s:1:"s";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"m";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:962;}}}}}}s:1:"t";a:2:{s:1:"h";a:1:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:977;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"a";a:1:{s:1:"n";a:1:{s:1:"g";a:1:{s:1:"l";a:1:{s:1:"e";a:2:{s:1:"l";a:1:{s:1:"e";a:1:{s:1:"f";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8882;}}}}}s:1:"r";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"h";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8883;}}}}}}}}}}}}}}}}s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1074;}}}s:1:"d";a:1:{s:1:"a";a:1:{s:1:"s";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8866;}}}}}s:1:"e";a:3:{s:1:"e";a:3:{s:1:";";a:1:{s:9:"codepoint";i:8744;}s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8891;}}}}s:1:"e";a:1:{s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8794;}}}}s:1:"l";a:1:{s:1:"l";a:1:{s:1:"i";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8942;}}}}}s:1:"r";a:2:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:124;}}}}s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:124;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120115;}}}s:1:"l";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8882;}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120167;}}}}s:1:"p";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8733;}}}}}s:1:"r";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8883;}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120011;}}}}s:1:"z";a:1:{s:1:"i";a:1:{s:1:"g";a:1:{s:1:"z";a:1:{s:1:"a";a:1:{s:1:"g";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10650;}}}}}}}}s:1:"w";a:7:{s:1:"c";a:1:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:373;}}}}}s:1:"e";a:2:{s:1:"d";a:2:{s:1:"b";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10847;}}}}s:1:"g";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8743;}s:1:"q";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8793;}}}}}s:1:"i";a:1:{s:1:"e";a:1:{s:1:"r";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8472;}}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120116;}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120168;}}}}s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8472;}}s:1:"r";a:2:{s:1:";";a:1:{s:9:"codepoint";i:8768;}s:1:"e";a:1:{s:1:"a";a:1:{s:1:"t";a:1:{s:1:"h";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8768;}}}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120012;}}}}}s:1:"x";a:14:{s:1:"c";a:3:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8898;}}}s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9711;}}}}s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8899;}}}}s:1:"d";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9661;}}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120117;}}}s:1:"h";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10234;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10231;}}}}}s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:958;}}s:1:"l";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10232;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10229;}}}}}s:1:"m";a:1:{s:1:"a";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10236;}}}}s:1:"n";a:1:{s:1:"i";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8955;}}}}s:1:"o";a:3:{s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10752;}}}}s:1:"p";a:2:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120169;}}s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10753;}}}}}s:1:"t";a:1:{s:1:"i";a:1:{s:1:"m";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10754;}}}}}}s:1:"r";a:2:{s:1:"A";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10233;}}}}s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10230;}}}}}s:1:"s";a:2:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120013;}}}s:1:"q";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"p";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10758;}}}}}}s:1:"u";a:2:{s:1:"p";a:1:{s:1:"l";a:1:{s:1:"u";a:1:{s:1:"s";a:1:{s:1:";";a:1:{s:9:"codepoint";i:10756;}}}}}s:1:"t";a:1:{s:1:"r";a:1:{s:1:"i";a:1:{s:1:";";a:1:{s:9:"codepoint";i:9651;}}}}}s:1:"v";a:1:{s:1:"e";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8897;}}}}s:1:"w";a:1:{s:1:"e";a:1:{s:1:"d";a:1:{s:1:"g";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8896;}}}}}}}s:1:"y";a:8:{s:1:"a";a:1:{s:1:"c";a:2:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:2:{s:1:";";a:1:{s:9:"codepoint";i:253;}s:9:"codepoint";i:253;}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1103;}}}}s:1:"c";a:2:{s:1:"i";a:1:{s:1:"r";a:1:{s:1:"c";a:1:{s:1:";";a:1:{s:9:"codepoint";i:375;}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1099;}}}s:1:"e";a:1:{s:1:"n";a:2:{s:1:";";a:1:{s:9:"codepoint";i:165;}s:9:"codepoint";i:165;}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120118;}}}s:1:"i";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1111;}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120170;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120014;}}}}s:1:"u";a:2:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1102;}}}s:1:"m";a:1:{s:1:"l";a:2:{s:1:";";a:1:{s:9:"codepoint";i:255;}s:9:"codepoint";i:255;}}}}s:1:"z";a:10:{s:1:"a";a:1:{s:1:"c";a:1:{s:1:"u";a:1:{s:1:"t";a:1:{s:1:"e";a:1:{s:1:";";a:1:{s:9:"codepoint";i:378;}}}}}}s:1:"c";a:2:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"o";a:1:{s:1:"n";a:1:{s:1:";";a:1:{s:9:"codepoint";i:382;}}}}}s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1079;}}}s:1:"d";a:1:{s:1:"o";a:1:{s:1:"t";a:1:{s:1:";";a:1:{s:9:"codepoint";i:380;}}}}s:1:"e";a:2:{s:1:"e";a:1:{s:1:"t";a:1:{s:1:"r";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8488;}}}}}s:1:"t";a:1:{s:1:"a";a:1:{s:1:";";a:1:{s:9:"codepoint";i:950;}}}}s:1:"f";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120119;}}}s:1:"h";a:1:{s:1:"c";a:1:{s:1:"y";a:1:{s:1:";";a:1:{s:9:"codepoint";i:1078;}}}}s:1:"i";a:1:{s:1:"g";a:1:{s:1:"r";a:1:{s:1:"a";a:1:{s:1:"r";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8669;}}}}}}}s:1:"o";a:1:{s:1:"p";a:1:{s:1:"f";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120171;}}}}s:1:"s";a:1:{s:1:"c";a:1:{s:1:"r";a:1:{s:1:";";a:1:{s:9:"codepoint";i:120015;}}}}s:1:"w";a:2:{s:1:"j";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8205;}}s:1:"n";a:1:{s:1:"j";a:1:{s:1:";";a:1:{s:9:"codepoint";i:8204;}}}}}}
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php b/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php
deleted file mode 100644
index 5d41906e3..000000000
--- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php
+++ /dev/null
@@ -1,308 +0,0 @@
-
- * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
- */
-
-namespace Svg\Surface;
-
-use Svg\Style;
-
-class SurfaceGmagick implements SurfaceInterface
-{
-    const DEBUG = false;
-
-    /** @var \GmagickDraw */
-    private $canvas;
-
-    private $width;
-    private $height;
-
-    /** @var Style */
-    private $style;
-
-    public function __construct($w, $h)
-    {
-        if (self::DEBUG) {
-            echo __FUNCTION__ . "\n";
-        }
-        $this->width = $w;
-        $this->height = $h;
-
-        $canvas = new \GmagickDraw();
-
-        $this->canvas = $canvas;
-    }
-
-    function out()
-    {
-        if (self::DEBUG) {
-            echo __FUNCTION__ . "\n";
-        }
-
-        $image = new \Gmagick();
-        $image->newimage($this->width, $this->height);
-        $image->drawimage($this->canvas);
-
-        $tmp = tempnam("", "gm");
-
-        $image->write($tmp);
-
-        return file_get_contents($tmp);
-    }
-
-    public function save()
-    {
-        if (self::DEBUG) {
-            echo __FUNCTION__ . "\n";
-        }
-        $this->canvas->save();
-    }
-
-    public function restore()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->restore();
-    }
-
-    public function scale($x, $y)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->scale($x, $y);
-    }
-
-    public function rotate($angle)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->rotate($angle);
-    }
-
-    public function translate($x, $y)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->translate($x, $y);
-    }
-
-    public function transform($a, $b, $c, $d, $e, $f)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->concat($a, $b, $c, $d, $e, $f);
-    }
-
-    public function beginPath()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        // TODO: Implement beginPath() method.
-    }
-
-    public function closePath()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->closepath();
-    }
-
-    public function fillStroke()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->fill_stroke();
-    }
-
-    public function clip()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->clip();
-    }
-
-    public function fillText($text, $x, $y, $maxWidth = null)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->set_text_pos($x, $y);
-        $this->canvas->show($text);
-    }
-
-    public function strokeText($text, $x, $y, $maxWidth = null)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        // TODO: Implement drawImage() method.
-    }
-
-    public function drawImage($image, $sx, $sy, $sw = null, $sh = null, $dx = null, $dy = null, $dw = null, $dh = null)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-
-        if (strpos($image, "data:") === 0) {
-            $data = substr($image, strpos($image, ";") + 1);
-            if (strpos($data, "base64") === 0) {
-                $data = base64_decode(substr($data, 7));
-            }
-
-            $image = tempnam("", "svg");
-            file_put_contents($image, $data);
-        }
-
-        $img = $this->canvas->load_image("auto", $image, "");
-
-        $sy = $sy - $sh;
-        $this->canvas->fit_image($img, $sx, $sy, 'boxsize={' . "$sw $sh" . '} fitmethod=entire');
-    }
-
-    public function lineTo($x, $y)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->lineto($x, $y);
-    }
-
-    public function moveTo($x, $y)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->moveto($x, $y);
-    }
-
-    public function quadraticCurveTo($cpx, $cpy, $x, $y)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        // TODO: Implement quadraticCurveTo() method.
-    }
-
-    public function bezierCurveTo($cp1x, $cp1y, $cp2x, $cp2y, $x, $y)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->curveto($cp1x, $cp1y, $cp2x, $cp2y, $x, $y);
-    }
-
-    public function arcTo($x1, $y1, $x2, $y2, $radius)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-    }
-
-    public function arc($x, $y, $radius, $startAngle, $endAngle, $anticlockwise = false)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->arc($x, $y, $radius, $startAngle, $endAngle);
-    }
-
-    public function circle($x, $y, $radius)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->circle($x, $y, $radius);
-    }
-
-    public function ellipse($x, $y, $radiusX, $radiusY, $rotation, $startAngle, $endAngle, $anticlockwise)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->ellipse($x, $y, $radiusX, $radiusY);
-    }
-
-    public function fillRect($x, $y, $w, $h)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->rect($x, $y, $w, $h);
-        $this->fill();
-    }
-
-    public function rect($x, $y, $w, $h, $rx = 0, $ry = 0)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->rect($x, $y, $w, $h);
-    }
-
-    public function fill()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->fill();
-    }
-
-    public function strokeRect($x, $y, $w, $h)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->rect($x, $y, $w, $h);
-        $this->stroke();
-    }
-
-    public function stroke()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $this->canvas->stroke();
-    }
-
-    public function endPath()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        //$this->canvas->endPath();
-    }
-
-    public function measureText($text)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-        $style = $this->getStyle();
-        $font = $this->getFont($style->fontFamily, $style->fontStyle);
-
-        return $this->canvas->stringwidth($text, $font, $this->getStyle()->fontSize);
-    }
-
-    public function getStyle()
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-
-        return $this->style;
-    }
-
-    public function setStyle(Style $style)
-    {
-        if (self::DEBUG) echo __FUNCTION__ . "\n";
-
-        $this->style = $style;
-        $canvas = $this->canvas;
-
-        if (is_array($style->stroke) && $stroke = $style->stroke) {
-            $canvas->setcolor("stroke", "rgb", $stroke[0] / 255, $stroke[1] / 255, $stroke[2] / 255, null);
-        }
-
-        if (is_array($style->fill) && $fill = $style->fill) {
-           // $canvas->setcolor("fill", "rgb", $fill[0] / 255, $fill[1] / 255, $fill[2] / 255, null);
-        }
-
-        $opts = array();
-        if ($style->strokeWidth > 0.000001) {
-            $opts[] = "linewidth=$style->strokeWidth";
-        }
-
-        if (in_array($style->strokeLinecap, array("butt", "round", "projecting"))) {
-            $opts[] = "linecap=$style->strokeLinecap";
-        }
-
-        if (in_array($style->strokeLinejoin, array("miter", "round", "bevel"))) {
-            $opts[] = "linejoin=$style->strokeLinejoin";
-        }
-
-        $canvas->set_graphics_option(implode(" ", $opts));
-
-        $font = $this->getFont($style->fontFamily, $style->fontStyle);
-        $canvas->setfont($font, $style->fontSize);
-    }
-
-    private function getFont($family, $style)
-    {
-        $map = array(
-            "serif"      => "Times",
-            "sans-serif" => "Helvetica",
-            "fantasy"    => "Symbol",
-            "cursive"    => "serif",
-            "monospance" => "Courier",
-        );
-
-        $family = strtolower($family);
-        if (isset($map[$family])) {
-            $family = $map[$family];
-        }
-
-        return $this->canvas->load_font($family, "unicode", "fontstyle=$style");
-    }
-
-    public function setFont($family, $style, $weight)
-    {
-        // TODO: Implement setFont() method.
-    }
-}
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/autoload.php b/library/vendor/dompdf/lib/php-svg-lib/src/autoload.php
deleted file mode 100644
index b0e2b9cc4..000000000
--- a/library/vendor/dompdf/lib/php-svg-lib/src/autoload.php
+++ /dev/null
@@ -1,17 +0,0 @@
-
- * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html
- */
-
-spl_autoload_register(function($class) {
-  if (0 === strpos($class, "Svg")) {
-    $file = str_replace('\\', DIRECTORY_SEPARATOR, $class);
-    $file = realpath(__DIR__ . DIRECTORY_SEPARATOR . $file . '.php');
-    if (file_exists($file)) {
-      include_once $file;
-    }
-  }
-});
\ No newline at end of file
diff --git a/library/vendor/dompdf/lib/res/broken_image.svg b/library/vendor/dompdf/lib/res/broken_image.svg
deleted file mode 100644
index 09b97e6c3..000000000
--- a/library/vendor/dompdf/lib/res/broken_image.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- 
-  
-  
-  
- 
-
\ No newline at end of file
diff --git a/library/vendor/dompdf/src/Adapter/CPDF.php b/library/vendor/dompdf/src/Adapter/CPDF.php
deleted file mode 100644
index d976627b8..000000000
--- a/library/vendor/dompdf/src/Adapter/CPDF.php
+++ /dev/null
@@ -1,1184 +0,0 @@
-
- * @author  Orion Richardson 
- * @author  Helmut Tischer 
- * @author  Fabien Ménager 
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
-// FIXME: Need to sanity check inputs to this class
-namespace Dompdf\Adapter;
-
-use Dompdf\Canvas;
-use Dompdf\Dompdf;
-use Dompdf\Helpers;
-use Dompdf\Exception;
-use Dompdf\Image\Cache;
-use Dompdf\PhpEvaluator;
-
-/**
- * PDF rendering interface
- *
- * Dompdf\Adapter\CPDF provides a simple stateless interface to the stateful one
- * provided by the Cpdf class.
- *
- * Unless otherwise mentioned, all dimensions are in points (1/72 in).  The
- * coordinate origin is in the top left corner, and y values increase
- * downwards.
- *
- * See {@link http://www.ros.co.nz/pdf/} for more complete documentation
- * on the underlying {@link Cpdf} class.
- *
- * @package dompdf
- */
-class CPDF implements Canvas
-{
-
-    /**
-     * Dimensions of paper sizes in points
-     *
-     * @var array;
-     */
-    static $PAPER_SIZES = array(
-        "4a0" => array(0, 0, 4767.87, 6740.79),
-        "2a0" => array(0, 0, 3370.39, 4767.87),
-        "a0" => array(0, 0, 2383.94, 3370.39),
-        "a1" => array(0, 0, 1683.78, 2383.94),
-        "a2" => array(0, 0, 1190.55, 1683.78),
-        "a3" => array(0, 0, 841.89, 1190.55),
-        "a4" => array(0, 0, 595.28, 841.89),
-        "a5" => array(0, 0, 419.53, 595.28),
-        "a6" => array(0, 0, 297.64, 419.53),
-        "a7" => array(0, 0, 209.76, 297.64),
-        "a8" => array(0, 0, 147.40, 209.76),
-        "a9" => array(0, 0, 104.88, 147.40),
-        "a10" => array(0, 0, 73.70, 104.88),
-        "b0" => array(0, 0, 2834.65, 4008.19),
-        "b1" => array(0, 0, 2004.09, 2834.65),
-        "b2" => array(0, 0, 1417.32, 2004.09),
-        "b3" => array(0, 0, 1000.63, 1417.32),
-        "b4" => array(0, 0, 708.66, 1000.63),
-        "b5" => array(0, 0, 498.90, 708.66),
-        "b6" => array(0, 0, 354.33, 498.90),
-        "b7" => array(0, 0, 249.45, 354.33),
-        "b8" => array(0, 0, 175.75, 249.45),
-        "b9" => array(0, 0, 124.72, 175.75),
-        "b10" => array(0, 0, 87.87, 124.72),
-        "c0" => array(0, 0, 2599.37, 3676.54),
-        "c1" => array(0, 0, 1836.85, 2599.37),
-        "c2" => array(0, 0, 1298.27, 1836.85),
-        "c3" => array(0, 0, 918.43, 1298.27),
-        "c4" => array(0, 0, 649.13, 918.43),
-        "c5" => array(0, 0, 459.21, 649.13),
-        "c6" => array(0, 0, 323.15, 459.21),
-        "c7" => array(0, 0, 229.61, 323.15),
-        "c8" => array(0, 0, 161.57, 229.61),
-        "c9" => array(0, 0, 113.39, 161.57),
-        "c10" => array(0, 0, 79.37, 113.39),
-        "ra0" => array(0, 0, 2437.80, 3458.27),
-        "ra1" => array(0, 0, 1729.13, 2437.80),
-        "ra2" => array(0, 0, 1218.90, 1729.13),
-        "ra3" => array(0, 0, 864.57, 1218.90),
-        "ra4" => array(0, 0, 609.45, 864.57),
-        "sra0" => array(0, 0, 2551.18, 3628.35),
-        "sra1" => array(0, 0, 1814.17, 2551.18),
-        "sra2" => array(0, 0, 1275.59, 1814.17),
-        "sra3" => array(0, 0, 907.09, 1275.59),
-        "sra4" => array(0, 0, 637.80, 907.09),
-        "letter" => array(0, 0, 612.00, 792.00),
-        "half-letter" => array(0, 0, 396.00, 612.00),
-        "legal" => array(0, 0, 612.00, 1008.00),
-        "ledger" => array(0, 0, 1224.00, 792.00),
-        "tabloid" => array(0, 0, 792.00, 1224.00),
-        "executive" => array(0, 0, 521.86, 756.00),
-        "folio" => array(0, 0, 612.00, 936.00),
-        "commercial #10 envelope" => array(0, 0, 684, 297),
-        "catalog #10 1/2 envelope" => array(0, 0, 648, 864),
-        "8.5x11" => array(0, 0, 612.00, 792.00),
-        "8.5x14" => array(0, 0, 612.00, 1008.0),
-        "11x17" => array(0, 0, 792.00, 1224.00),
-    );
-
-    /**
-     * The Dompdf object
-     *
-     * @var Dompdf
-     */
-    private $_dompdf;
-
-    /**
-     * Instance of Cpdf class
-     *
-     * @var Cpdf
-     */
-    private $_pdf;
-
-    /**
-     * PDF width, in points
-     *
-     * @var float
-     */
-    private $_width;
-
-    /**
-     * PDF height, in points
-     *
-     * @var float;
-     */
-    private $_height;
-
-    /**
-     * Current page number
-     *
-     * @var int
-     */
-    private $_page_number;
-
-    /**
-     * Total number of pages
-     *
-     * @var int
-     */
-    private $_page_count;
-
-    /**
-     * Text to display on every page
-     *
-     * @var array
-     */
-    private $_page_text;
-
-    /**
-     * Array of pages for accessing after rendering is initially complete
-     *
-     * @var array
-     */
-    private $_pages;
-
-    /**
-     * Array of temporary cached images to be deleted when processing is complete
-     *
-     * @var array
-     */
-    private $_image_cache;
-
-    /**
-     * Currently-applied opacity level (0 - 1)
-     *
-     * @var float
-     */
-    private $_current_opacity = 1;
-
-    /**
-     * Class constructor
-     *
-     * @param mixed $paper The size of paper to use in this PDF ({@link CPDF::$PAPER_SIZES})
-     * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
-     * @param Dompdf $dompdf The Dompdf instance
-     */
-    public function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf)
-    {
-        if (is_array($paper)) {
-            $size = $paper;
-        } else if (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) {
-            $size = self::$PAPER_SIZES[mb_strtolower($paper)];
-        } else {
-            $size = self::$PAPER_SIZES["letter"];
-        }
-
-        if (mb_strtolower($orientation) === "landscape") {
-            list($size[2], $size[3]) = array($size[3], $size[2]);
-        }
-
-        $this->_dompdf = $dompdf;
-
-        $this->_pdf = new \Cpdf(
-            $size,
-            true,
-            $dompdf->getOptions()->getFontCache(),
-            $dompdf->getOptions()->getTempDir()
-        );
-
-        $this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $dompdf->version));
-        $time = substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
-        $this->_pdf->addInfo("CreationDate", "D:$time");
-        $this->_pdf->addInfo("ModDate", "D:$time");
-
-        $this->_width = $size[2] - $size[0];
-        $this->_height = $size[3] - $size[1];
-
-        $this->_page_number = $this->_page_count = 1;
-        $this->_page_text = array();
-
-        $this->_pages = array($this->_pdf->getFirstPageId());
-
-        $this->_image_cache = array();
-    }
-
-    /**
-     * @return Dompdf
-     */
-    public function get_dompdf()
-    {
-        return $this->_dompdf;
-    }
-
-    /**
-     * Class destructor
-     *
-     * Deletes all temporary image files
-     */
-    public function __destruct()
-    {
-        foreach ($this->_image_cache as $img) {
-            // The file might be already deleted by 3rd party tmp cleaner,
-            // the file might not have been created at all
-            // (if image outputting commands failed)
-            // or because the destructor was called twice accidentally.
-            if (!file_exists($img)) {
-                continue;
-            }
-
-            if ($this->_dompdf->getOptions()->getDebugPng()) {
-                print '[__destruct unlink ' . $img . ']';
-            }
-            if (!$this->_dompdf->getOptions()->getDebugKeepTemp()) {
-                unlink($img);
-            }
-        }
-    }
-
-    /**
-     * Returns the Cpdf instance
-     *
-     * @return \Cpdf
-     */
-    public function get_cpdf()
-    {
-        return $this->_pdf;
-    }
-
-    /**
-     * Add meta information to the PDF
-     *
-     * @param string $label label of the value (Creator, Producer, etc.)
-     * @param string $value the text to set
-     */
-    public function add_info($label, $value)
-    {
-        $this->_pdf->addInfo($label, $value);
-    }
-
-    /**
-     * Opens a new 'object'
-     *
-     * While an object is open, all drawing actions are recorded in the object,
-     * as opposed to being drawn on the current page.  Objects can be added
-     * later to a specific page or to several pages.
-     *
-     * The return value is an integer ID for the new object.
-     *
-     * @see CPDF::close_object()
-     * @see CPDF::add_object()
-     *
-     * @return int
-     */
-    public function open_object()
-    {
-        $ret = $this->_pdf->openObject();
-        $this->_pdf->saveState();
-        return $ret;
-    }
-
-    /**
-     * Reopens an existing 'object'
-     *
-     * @see CPDF::open_object()
-     * @param int $object the ID of a previously opened object
-     */
-    public function reopen_object($object)
-    {
-        $this->_pdf->reopenObject($object);
-        $this->_pdf->saveState();
-    }
-
-    /**
-     * Closes the current 'object'
-     *
-     * @see CPDF::open_object()
-     */
-    public function close_object()
-    {
-        $this->_pdf->restoreState();
-        $this->_pdf->closeObject();
-    }
-
-    /**
-     * Adds a specified 'object' to the document
-     *
-     * $object int specifying an object created with {@link
-     * CPDF::open_object()}.  $where can be one of:
-     * - 'add' add to current page only
-     * - 'all' add to every page from the current one onwards
-     * - 'odd' add to all odd numbered pages from now on
-     * - 'even' add to all even numbered pages from now on
-     * - 'next' add the object to the next page only
-     * - 'nextodd' add to all odd numbered pages from the next one
-     * - 'nexteven' add to all even numbered pages from the next one
-     *
-     * @see Cpdf::addObject()
-     *
-     * @param int $object
-     * @param string $where
-     */
-    public function add_object($object, $where = 'all')
-    {
-        $this->_pdf->addObject($object, $where);
-    }
-
-    /**
-     * Stops the specified 'object' from appearing in the document.
-     *
-     * The object will stop being displayed on the page following the current
-     * one.
-     *
-     * @param int $object
-     */
-    public function stop_object($object)
-    {
-        $this->_pdf->stopObject($object);
-    }
-
-    /**
-     * @access private
-     */
-    public function serialize_object($id)
-    {
-        // Serialize the pdf object's current state for retrieval later
-        return $this->_pdf->serializeObject($id);
-    }
-
-    /**
-     * @access private
-     */
-    public function reopen_serialized_object($obj)
-    {
-        return $this->_pdf->restoreSerializedObject($obj);
-    }
-
-    //........................................................................
-
-    /**
-     * Returns the PDF's width in points
-     * @return float
-     */
-    public function get_width()
-    {
-        return $this->_width;
-    }
-
-    /**
-     * Returns the PDF's height in points
-     * @return float
-     */
-    public function get_height()
-    {
-        return $this->_height;
-    }
-
-    /**
-     * Returns the current page number
-     * @return int
-     */
-    public function get_page_number()
-    {
-        return $this->_page_number;
-    }
-
-    /**
-     * Returns the total number of pages in the document
-     * @return int
-     */
-    public function get_page_count()
-    {
-        return $this->_page_count;
-    }
-
-    /**
-     * Sets the current page number
-     *
-     * @param int $num
-     */
-    public function set_page_number($num)
-    {
-        $this->_page_number = $num;
-    }
-
-    /**
-     * Sets the page count
-     *
-     * @param int $count
-     */
-    public function set_page_count($count)
-    {
-        $this->_page_count = $count;
-    }
-
-    /**
-     * Sets the stroke color
-     *
-     * See {@link Style::set_color()} for the format of the color array.
-     * @param array $color
-     */
-    protected function _set_stroke_color($color)
-    {
-        $this->_pdf->setStrokeColor($color);
-        $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
-        if ($this->_current_opacity != 1) {
-            $alpha *= $this->_current_opacity;
-        }
-        $this->_set_line_transparency("Normal", $alpha);
-    }
-
-    /**
-     * Sets the fill colour
-     *
-     * See {@link Style::set_color()} for the format of the colour array.
-     * @param array $color
-     */
-    protected function _set_fill_color($color)
-    {
-        $this->_pdf->setColor($color);
-        $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
-        if ($this->_current_opacity) {
-            $alpha *= $this->_current_opacity;
-        }
-        $this->_set_fill_transparency("Normal", $alpha);
-    }
-
-    /**
-     * Sets line transparency
-     * @see Cpdf::setLineTransparency()
-     *
-     * Valid blend modes are (case-sensitive):
-     *
-     * Normal, Multiply, Screen, Overlay, Darken, Lighten,
-     * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
-     * Exclusion
-     *
-     * @param string $mode the blending mode to use
-     * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
-     */
-    protected function _set_line_transparency($mode, $opacity)
-    {
-        $this->_pdf->setLineTransparency($mode, $opacity);
-    }
-
-    /**
-     * Sets fill transparency
-     * @see Cpdf::setFillTransparency()
-     *
-     * Valid blend modes are (case-sensitive):
-     *
-     * Normal, Multiply, Screen, Overlay, Darken, Lighten,
-     * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
-     * Exclusion
-     *
-     * @param string $mode the blending mode to use
-     * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
-     */
-    protected function _set_fill_transparency($mode, $opacity)
-    {
-        $this->_pdf->setFillTransparency($mode, $opacity);
-    }
-
-    /**
-     * Sets the line style
-     *
-     * @see Cpdf::setLineStyle()
-     *
-     * @param float $width
-     * @param string $cap
-     * @param string $join
-     * @param array $dash
-     */
-    protected function _set_line_style($width, $cap, $join, $dash)
-    {
-        $this->_pdf->setLineStyle($width, $cap, $join, $dash);
-    }
-
-    /**
-     * Sets the opacity
-     *
-     * @param $opacity
-     * @param $mode
-     */
-    public function set_opacity($opacity, $mode = "Normal")
-    {
-        $this->_set_line_transparency($mode, $opacity);
-        $this->_set_fill_transparency($mode, $opacity);
-        $this->_current_opacity = $opacity;
-    }
-
-    public function set_default_view($view, $options = array())
-    {
-        array_unshift($options, $view);
-        call_user_func_array(array($this->_pdf, "openHere"), $options);
-    }
-
-    /**
-     * Remaps y coords from 4th to 1st quadrant
-     *
-     * @param float $y
-     * @return float
-     */
-    protected function y($y)
-    {
-        return $this->_height - $y;
-    }
-
-    /**
-     * Canvas implementation
-     *
-     * @param float $x1
-     * @param float $y1
-     * @param float $x2
-     * @param float $y2
-     * @param array $color
-     * @param float $width
-     * @param array $style
-     */
-    public function line($x1, $y1, $x2, $y2, $color, $width, $style = array())
-    {
-        $this->_set_stroke_color($color);
-        $this->_set_line_style($width, "butt", "", $style);
-
-        $this->_pdf->line($x1, $this->y($y1),
-            $x2, $this->y($y2));
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
-    }
-
-    /**
-     * Draw line at the specified coordinates on every page.
-     *
-     * See {@link Style::munge_color()} for the format of the colour array.
-     *
-     * @param float $x1
-     * @param float $y1
-     * @param float $x2
-     * @param float $y2
-     * @param array $color
-     * @param float $width
-     * @param array $style optional
-     */
-    public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = array())
-    {
-        $_t = 'line';
-        $this->_page_text[] = compact('_t', 'x1', 'y1', 'x2', 'y2', 'color', 'width', 'style');
-    }
-
-    /**
-     * @param float $x
-     * @param float $y
-     * @param float $r1
-     * @param float $r2
-     * @param float $astart
-     * @param float $aend
-     * @param array $color
-     * @param float $width
-     * @param array $style
-     */
-    public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array())
-    {
-        $this->_set_stroke_color($color);
-        $this->_set_line_style($width, "butt", "", $style);
-
-        $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
-    }
-
-    /**
-     * Convert a GIF or BMP image to a PNG image
-     *
-     * @param string $image_url
-     * @param integer $type
-     *
-     * @throws Exception
-     * @return string The url of the newly converted image
-     */
-    protected function _convert_gif_bmp_to_png($image_url, $type)
-    {
-        $func_name = "imagecreatefrom$type";
-
-        if (!function_exists($func_name)) {
-            if (!method_exists("Dompdf\Helpers", $func_name)) {
-                throw new Exception("Function $func_name() not found.  Cannot convert $type image: $image_url.  Please install the image PHP extension.");
-            }
-            $func_name = "\\Dompdf\\Helpers::" . $func_name;
-        }
-
-        set_error_handler(array("\\Dompdf\\Helpers", "record_warnings"));
-        $im = call_user_func($func_name, $image_url);
-
-        if ($im) {
-            imageinterlace($im, false);
-
-            $tmp_dir = $this->_dompdf->getOptions()->getTempDir();
-            $tmp_name = @tempnam($tmp_dir, "{$type}dompdf_img_");
-            @unlink($tmp_name);
-            $filename = "$tmp_name.png";
-            $this->_image_cache[] = $filename;
-
-            imagepng($im, $filename);
-            imagedestroy($im);
-        } else {
-            $filename = Cache::$broken_image;
-        }
-
-        restore_error_handler();
-
-        return $filename;
-    }
-
-    /**
-     * @param float $x1
-     * @param float $y1
-     * @param float $w
-     * @param float $h
-     * @param array $color
-     * @param float $width
-     * @param array $style
-     */
-    public function rectangle($x1, $y1, $w, $h, $color, $width, $style = array())
-    {
-        $this->_set_stroke_color($color);
-        $this->_set_line_style($width, "butt", "", $style);
-        $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
-    }
-
-    /**
-     * @param float $x1
-     * @param float $y1
-     * @param float $w
-     * @param float $h
-     * @param array $color
-     */
-    public function filled_rectangle($x1, $y1, $w, $h, $color)
-    {
-        $this->_set_fill_color($color);
-        $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
-        $this->_set_fill_transparency("Normal", $this->_current_opacity);
-    }
-
-    /**
-     * @param float $x1
-     * @param float $y1
-     * @param float $w
-     * @param float $h
-     */
-    public function clipping_rectangle($x1, $y1, $w, $h)
-    {
-        $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
-    }
-
-    /**
-     * @param float $x1
-     * @param float $y1
-     * @param float $w
-     * @param float $h
-     * @param float $rTL
-     * @param float $rTR
-     * @param float $rBR
-     * @param float $rBL
-     */
-    public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
-    {
-        $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
-    }
-
-    /**
-     *
-     */
-    public function clipping_end()
-    {
-        $this->_pdf->clippingEnd();
-    }
-
-    /**
-     *
-     */
-    public function save()
-    {
-        $this->_pdf->saveState();
-    }
-
-    /**
-     *
-     */
-    public function restore()
-    {
-        $this->_pdf->restoreState();
-    }
-
-    /**
-     * @param $angle
-     * @param $x
-     * @param $y
-     */
-    public function rotate($angle, $x, $y)
-    {
-        $this->_pdf->rotate($angle, $x, $y);
-    }
-
-    /**
-     * @param $angle_x
-     * @param $angle_y
-     * @param $x
-     * @param $y
-     */
-    public function skew($angle_x, $angle_y, $x, $y)
-    {
-        $this->_pdf->skew($angle_x, $angle_y, $x, $y);
-    }
-
-    /**
-     * @param $s_x
-     * @param $s_y
-     * @param $x
-     * @param $y
-     */
-    public function scale($s_x, $s_y, $x, $y)
-    {
-        $this->_pdf->scale($s_x, $s_y, $x, $y);
-    }
-
-    /**
-     * @param $t_x
-     * @param $t_y
-     */
-    public function translate($t_x, $t_y)
-    {
-        $this->_pdf->translate($t_x, $t_y);
-    }
-
-    /**
-     * @param $a
-     * @param $b
-     * @param $c
-     * @param $d
-     * @param $e
-     * @param $f
-     */
-    public function transform($a, $b, $c, $d, $e, $f)
-    {
-        $this->_pdf->transform(array($a, $b, $c, $d, $e, $f));
-    }
-
-    /**
-     * @param array $points
-     * @param array $color
-     * @param null $width
-     * @param array $style
-     * @param bool $fill
-     */
-    public function polygon($points, $color, $width = null, $style = array(), $fill = false)
-    {
-        $this->_set_fill_color($color);
-        $this->_set_stroke_color($color);
-
-        // Adjust y values
-        for ($i = 1; $i < count($points); $i += 2) {
-            $points[$i] = $this->y($points[$i]);
-        }
-
-        $this->_pdf->polygon($points, count($points) / 2, $fill);
-
-        $this->_set_fill_transparency("Normal", $this->_current_opacity);
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
-    }
-
-    /**
-     * @param float $x
-     * @param float $y
-     * @param float $r1
-     * @param array $color
-     * @param null $width
-     * @param null $style
-     * @param bool $fill
-     */
-    public function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false)
-    {
-        $this->_set_fill_color($color);
-        $this->_set_stroke_color($color);
-
-        if (!$fill && isset($width)) {
-            $this->_set_line_style($width, "round", "round", $style);
-        }
-
-        $this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill);
-
-        $this->_set_fill_transparency("Normal", $this->_current_opacity);
-        $this->_set_line_transparency("Normal", $this->_current_opacity);
-    }
-
-    /**
-     * @param string $img
-     * @param float $x
-     * @param float $y
-     * @param int $w
-     * @param int $h
-     * @param string $resolution
-     */
-    public function image($img, $x, $y, $w, $h, $resolution = "normal")
-    {
-        list($width, $height, $type) = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
-
-        $debug_png = $this->_dompdf->getOptions()->getDebugPng();
-
-        if ($debug_png) {
-            print "[image:$img|$width|$height|$type]";
-        }
-
-        switch ($type) {
-            case "jpeg":
-                if ($debug_png) {
-                    print '!!!jpg!!!';
-                }
-                $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
-                break;
-
-            case "gif":
-            /** @noinspection PhpMissingBreakStatementInspection */
-            case "bmp":
-                if ($debug_png) print '!!!bmp or gif!!!';
-                // @todo use cache for BMP and GIF
-                $img = $this->_convert_gif_bmp_to_png($img, $type);
-
-            case "png":
-                if ($debug_png) print '!!!png!!!';
-
-                $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
-                break;
-
-            case "svg":
-                if ($debug_png) print '!!!SVG!!!';
-
-                $this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h);
-                break;
-
-            default:
-                if ($debug_png) print '!!!unknown!!!';
-        }
-    }
-
-    /**
-     * @param float $x
-     * @param float $y
-     * @param string $text
-     * @param string $font
-     * @param float $size
-     * @param array $color
-     * @param float $word_space
-     * @param float $char_space
-     * @param float $angle
-     */
-    public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
-    {
-        $pdf = $this->_pdf;
-
-        $this->_set_fill_color($color);
-
-        $font .= ".afm";
-        $pdf->selectFont($font);
-
-        //FontMetrics::getFontHeight($font, $size) ==
-        //$this->getFontHeight($font, $size) ==
-        //$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size)
-        //- FontBBoxheight+FontHeightOffset, scaled to $size, in pt
-        //$this->_pdf->getFontDescender($size)
-        //- Descender scaled to size
-        //
-        //$this->_pdf->fonts[$this->_pdf->currentFont] sizes:
-        //['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top
-        //Maximum extent of all glyphs of the font from the baseline point
-        //['Ascender'] maximum height above baseline except accents
-        //['Descender'] maximum depth below baseline, negative number means below baseline
-        //['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used.
-        //Values are in 1/1000 pt for a font size of 1 pt
-        //
-        //['FontBBox'][1] should be close to ['Descender']
-        //['FontBBox'][3] should be close to ['Ascender']+Accents
-        //in practice, FontBBox values are a little bigger
-        //
-        //The text position is referenced to the baseline, not to the lower corner of the FontBBox,
-        //for what the left,top corner is given.
-        //FontBBox spans also the background box for the text.
-        //If the lower corner would be used as reference point, the Descents of the glyphs would
-        //hang over the background box border.
-        //Therefore compensate only the extent above the Baseline.
-        //
-        //print '
['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']
'; - // - //$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space); - $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space); - - $this->_set_fill_transparency("Normal", $this->_current_opacity); - } - - /** - * @param string $code - */ - public function javascript($code) - { - $this->_pdf->addJavascript($code); - } - - //........................................................................ - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - public function add_named_dest($anchorname) - { - $this->_pdf->addDestination($anchorname, "Fit"); - } - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - public function add_link($url, $x, $y, $width, $height) - { - $y = $this->y($y) - $height; - - if (strpos($url, '#') === 0) { - // Local link - $name = substr($url, 1); - if ($name) { - $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height); - } - } else { - $this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height); - } - } - - /** - * @param string $text - * @param string $font - * @param float $size - * @param int $word_spacing - * @param int $char_spacing - * @return float|int - */ - public function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) - { - $this->_pdf->selectFont($font); - return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing); - } - - /** - * @param $font - * @param $string - */ - public function register_string_subset($font, $string) - { - $this->_pdf->registerText($font, $string); - } - - /** - * @param string $font - * @param float $size - * @return float|int - */ - public function get_font_height($font, $size) - { - $this->_pdf->selectFont($font); - - $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); - return $this->_pdf->getFontHeight($size) * $ratio; - } - - /*function get_font_x_height($font, $size) { - $this->_pdf->selectFont($font); - $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); - return $this->_pdf->getFontXHeight($size) * $ratio; - }*/ - - /** - * @param string $font - * @param float $size - * @return float - */ - public function get_font_baseline($font, $size) - { - $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); - return $this->get_font_height($font, $size) / $ratio; - } - - /** - * Writes text at the specified x and y coordinates on every page - * - * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced - * with their current values. - * - * See {@link Style::munge_color()} for the format of the colour array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle to write the text at, measured CW starting from the x-axis - */ - public function page_text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) - { - $_t = "text"; - $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle"); - } - - /** - * Processes a script on every page - * - * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available. - * - * This function can be used to add page numbers to all pages - * after the first one, for example. - * - * @param string $code the script code - * @param string $type the language type for script - */ - public function page_script($code, $type = "text/php") - { - $_t = "script"; - $this->_page_text[] = compact("_t", "code", "type"); - } - - /** - * @return int - */ - public function new_page() - { - $this->_page_number++; - $this->_page_count++; - - $ret = $this->_pdf->newPage(); - $this->_pages[] = $ret; - return $ret; - } - - /** - * Add text to each page after rendering is complete - */ - protected function _add_page_text() - { - if (!count($this->_page_text)) { - return; - } - - $page_number = 1; - $eval = null; - - foreach ($this->_pages as $pid) { - $this->reopen_object($pid); - - foreach ($this->_page_text as $pt) { - extract($pt); - - switch ($_t) { - case "text": - $text = str_replace(array("{PAGE_NUM}", "{PAGE_COUNT}"), - array($page_number, $this->_page_count), $text); - $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - break; - - case "script": - if (!$eval) { - $eval = new PhpEvaluator($this); - } - $eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count)); - break; - - case 'line': - $this->line( $x1, $y1, $x2, $y2, $color, $width, $style ); - break; - } - } - - $this->close_object(); - $page_number++; - } - } - - /** - * Streams the PDF to the client. - * - * @param string $filename The filename to present to the client. - * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). - */ - public function stream($filename = "document.pdf", $options = array()) - { - if (headers_sent()) { - die("Unable to stream pdf: headers already sent"); - } - - if (!isset($options["compress"])) $options["compress"] = true; - if (!isset($options["Attachment"])) $options["Attachment"] = true; - - $this->_add_page_text(); - - $debug = !$options['compress']; - $tmp = ltrim($this->_pdf->output($debug)); - - header("Cache-Control: private"); - header("Content-Type: application/pdf"); - header("Content-Length: " . mb_strlen($tmp, "8bit")); - - $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf"; - $attachment = $options["Attachment"] ? "attachment" : "inline"; - header(Helpers::buildContentDispositionHeader($attachment, $filename)); - - echo $tmp; - flush(); - } - - /** - * Returns the PDF as a string. - * - * @param array $options Associative array: 'compress' => 1 or 0 (default 1). - * @return string - */ - public function output($options = array()) - { - if (!isset($options["compress"])) $options["compress"] = true; - - $this->_add_page_text(); - - $debug = !$options['compress']; - - return $this->_pdf->output($debug); - } - - /** - * Returns logging messages generated by the Cpdf class - * - * @return string - */ - public function get_messages() - { - return $this->_pdf->messages; - } -} diff --git a/library/vendor/dompdf/src/Autoloader.php b/library/vendor/dompdf/src/Autoloader.php deleted file mode 100644 index 08a3b4679..000000000 --- a/library/vendor/dompdf/src/Autoloader.php +++ /dev/null @@ -1,42 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -namespace Dompdf; - -/** - * Main rendering interface - * - * Currently {@link Dompdf\Adapter\CPDF}, {@link Dompdf\Adapter\PDFLib}, and {@link Dompdf\Adapter\GD} - * implement this interface. - * - * Implementations should measure x and y increasing to the left and down, - * respectively, with the origin in the top left corner. Implementations - * are free to use a unit other than points for length, but I can't - * guarantee that the results will look any good. - * - * @package dompdf - */ -interface Canvas -{ - function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf); - - /** - * @return Dompdf - */ - function get_dompdf(); - - /** - * Returns the current page number - * - * @return int - */ - function get_page_number(); - - /** - * Returns the total number of pages - * - * @return int - */ - function get_page_count(); - - /** - * Sets the total number of pages - * - * @param int $count - */ - function set_page_count($count); - - /** - * Draws a line from x1,y1 to x2,y2 - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the format of the - * $style parameter (aka dash). - * - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - function line($x1, $y1, $x2, $y2, $color, $width, $style = null); - - /** - * Draws a rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - * @param float $width - * @param array $style - */ - function rectangle($x1, $y1, $w, $h, $color, $width, $style = null); - - /** - * Draws a filled rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - */ - function filled_rectangle($x1, $y1, $w, $h, $color); - - /** - * Starts a clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - */ - function clipping_rectangle($x1, $y1, $w, $h); - - /** - * Starts a rounded clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param float $tl - * @param float $tr - * @param float $br - * @param float $bl - * - * @return - */ - function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl); - - /** - * Ends the last clipping shape - */ - function clipping_end(); - - /** - * Save current state - */ - function save(); - - /** - * Restore last state - */ - function restore(); - - /** - * Rotate - * - * @param float $angle angle in degrees for counter-clockwise rotation - * @param float $x Origin abscissa - * @param float $y Origin ordinate - */ - function rotate($angle, $x, $y); - - /** - * Skew - * - * @param float $angle_x - * @param float $angle_y - * @param float $x Origin abscissa - * @param float $y Origin ordinate - */ - function skew($angle_x, $angle_y, $x, $y); - - /** - * Scale - * - * @param float $s_x scaling factor for width as percent - * @param float $s_y scaling factor for height as percent - * @param float $x Origin abscissa - * @param float $y Origin ordinate - */ - function scale($s_x, $s_y, $x, $y); - - /** - * Translate - * - * @param float $t_x movement to the right - * @param float $t_y movement to the bottom - */ - function translate($t_x, $t_y); - - /** - * Transform - * - * @param $a - * @param $b - * @param $c - * @param $d - * @param $e - * @param $f - * @return - */ - function transform($a, $b, $c, $d, $e, $f); - - /** - * Draws a polygon - * - * The polygon is formed by joining all the points stored in the $points - * array. $points has the following structure: - * - * array(0 => x1, - * 1 => y1, - * 2 => x2, - * 3 => y2, - * ... - * ); - * - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param array $points - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the polygon if true - */ - function polygon($points, $color, $width = null, $style = null, $fill = false); - - /** - * Draws a circle at $x,$y with radius $r - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x - * @param float $y - * @param float $r - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the circle if true - */ - function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false); - - /** - * Add an image to the pdf. - * - * The image is placed at the specified x and y coordinates with the - * given width and height. - * - * @param string $img_url the path to the image - * @param float $x x position - * @param float $y y position - * @param int $w width (in pixels) - * @param int $h height (in pixels) - * @param string $resolution The resolution of the image - */ - function image($img_url, $x, $y, $w, $h, $resolution = "normal"); - - /** - * Add an arc to the PDF - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x X coordinate of the arc - * @param float $y Y coordinate of the arc - * @param float $r1 Radius 1 - * @param float $r2 Radius 2 - * @param float $astart Start angle in degrees - * @param float $aend End angle in degrees - * @param array $color Color - * @param float $width - * @param array $style - */ - function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()); - - /** - * Writes text at the specified x and y coordinates - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle - */ - function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0); - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname); - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - function add_link($url, $x, $y, $width, $height); - - /** - * Add meta information to the pdf - * - * @param string $name Label of the value (Creator, Producer, etc.) - * @param string $value The text to set - */ - function add_info($name, $value); - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing word spacing, if any - * @param float $char_spacing - * - * @return float - */ - function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0); - - /** - * Calculates font height, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - function get_font_height($font, $size); - - /** - * Calculates font baseline, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - function get_font_baseline($font, $size); - - /** - * Returns the PDF's width in points - * - * @return float - */ - function get_width(); - - - /** - * Return the image's height in pixels - * - * @return float - */ - function get_height(); - - /** - * Returns the font x-height, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - //function get_font_x_height($font, $size); - - /** - * Sets the opacity - * - * @param float $opacity - * @param string $mode - */ - function set_opacity($opacity, $mode = "Normal"); - - /** - * Sets the default view - * - * @param string $view - * 'XYZ' left, top, zoom - * 'Fit' - * 'FitH' top - * 'FitV' left - * 'FitR' left,bottom,right - * 'FitB' - * 'FitBH' top - * 'FitBV' left - * @param array $options - * - * @return void - */ - function set_default_view($view, $options = array()); - - /** - * @param string $script - * - * @return void - */ - function javascript($script); - - /** - * Starts a new page - * - * Subsequent drawing operations will appear on the new page. - */ - function new_page(); - - /** - * Streams the PDF directly to the browser. - * - * @param string $filename The filename to present to the browser. - * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). - */ - function stream($filename, $options = array()); - - /** - * Returns the PDF as a string. - * - * @param array $options Associative array: 'compress' => 1 or 0 (default 1). - * @return string - */ - function output($options = array()); -} diff --git a/library/vendor/dompdf/src/Css/Style.php b/library/vendor/dompdf/src/Css/Style.php deleted file mode 100644 index 96ac5d42b..000000000 --- a/library/vendor/dompdf/src/Css/Style.php +++ /dev/null @@ -1,2952 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\Css; - -use Dompdf\Adapter\CPDF; -use Dompdf\Exception; -use Dompdf\Helpers; -use Dompdf\FontMetrics; -use Dompdf\Frame; - -/** - * Represents CSS properties. - * - * The Style class is responsible for handling and storing CSS properties. - * It includes methods to resolve colors and lengths, as well as getters & - * setters for many CSS properites. - * - * Actual CSS parsing is performed in the {@link Stylesheet} class. - * - * @package dompdf - */ -class Style -{ - - const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*"; - const CSS_INTEGER = "-?\d+"; - - /** - * Default font size, in points. - * - * @var float - */ - static $default_font_size = 12; - - /** - * Default line height, as a fraction of the font size. - * - * @var float - */ - static $default_line_height = 1.2; - - /** - * Default "absolute" font sizes relative to the default font-size - * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property - * @var array - */ - static $font_size_keywords = array( - "xx-small" => 0.6, // 3/5 - "x-small" => 0.75, // 3/4 - "small" => 0.889, // 8/9 - "medium" => 1, // 1 - "large" => 1.2, // 6/5 - "x-large" => 1.5, // 3/2 - "xx-large" => 2.0, // 2/1 - ); - - /** - * List of valid vertical-align keywords. Should also really be a constant. - * - * @var array - */ - static $vertical_align_keywords = array("baseline", "bottom", "middle", "sub", - "super", "text-bottom", "text-top", "top"); - - /** - * List of all inline types. Should really be a constant. - * - * @var array - */ - static $INLINE_TYPES = array("inline"); - - /** - * List of all block types. Should really be a constant. - * - * @var array - */ - static $BLOCK_TYPES = array("block", "inline-block", "table-cell", "list-item"); - - /** - * List of all positionned types. Should really be a constant. - * - * @var array - */ - static $POSITIONNED_TYPES = array("relative", "absolute", "fixed"); - - /** - * List of all table types. Should really be a constant. - * - * @var array; - */ - static $TABLE_TYPES = array("table", "inline-table"); - - /** - * List of valid border styles. Should also really be a constant. - * - * @var array - */ - static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid", - "double", "groove", "ridge", "inset", "outset"); - - /** - * Default style values. - * - * @link http://www.w3.org/TR/CSS21/propidx.html - * - * @var array - */ - static protected $_defaults = null; - - /** - * List of inherited properties - * - * @link http://www.w3.org/TR/CSS21/propidx.html - * - * @var array - */ - static protected $_inherited = null; - - /** - * Caches method_exists result - * - * @var array - */ - static protected $_methods_cache = array(); - - /** - * The stylesheet this style belongs to - * - * @see Stylesheet - * @var Stylesheet - */ - protected $_stylesheet; // stylesheet this style is attached to - - /** - * Media queries attached to the style - * - * @var int - */ - protected $_media_queries; - - /** - * Main array of all CSS properties & values - * - * @var array - */ - protected $_props; - - /* var instead of protected would allow access outside of class */ - protected $_important_props; - - /** - * Cached property values - * - * @var array - */ - protected $_prop_cache; - - /** - * Font size of parent element in document tree. Used for relative font - * size resolution. - * - * @var float - */ - protected $_parent_font_size; // Font size of parent element - - protected $_font_family; - - /** - * @var Frame - */ - protected $_frame; - - /** - * The origin of the style - * - * @var int - */ - protected $_origin = Stylesheet::ORIG_AUTHOR; - - // private members - /** - * True once the font size is resolved absolutely - * - * @var bool - */ - private $__font_size_calculated; // Cache flag - - /** - * The computed bottom spacing - */ - private $_computed_bottom_spacing = null; - - /** - * The computed border radius - */ - private $_computed_border_radius = null; - - /** - * @var bool - */ - public $_has_border_radius = false; - - /** - * @var FontMetrics - */ - private $fontMetrics; - - /** - * Class constructor - * - * @param Stylesheet $stylesheet the stylesheet this Style is associated with. - * @param int $origin - */ - public function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR) - { - $this->setFontMetrics($stylesheet->getFontMetrics()); - - $this->_props = array(); - $this->_important_props = array(); - $this->_stylesheet = $stylesheet; - $this->_media_queries = array(); - $this->_origin = $origin; - $this->_parent_font_size = null; - $this->__font_size_calculated = false; - - if (!isset(self::$_defaults)) { - - // Shorthand - $d =& self::$_defaults; - - // All CSS 2.1 properties, and their default values - $d["azimuth"] = "center"; - $d["background_attachment"] = "scroll"; - $d["background_color"] = "transparent"; - $d["background_image"] = "none"; - $d["background_image_resolution"] = "normal"; - $d["_dompdf_background_image_resolution"] = $d["background_image_resolution"]; - $d["background_position"] = "0% 0%"; - $d["background_repeat"] = "repeat"; - $d["background"] = ""; - $d["border_collapse"] = "separate"; - $d["border_color"] = ""; - $d["border_spacing"] = "0"; - $d["border_style"] = ""; - $d["border_top"] = ""; - $d["border_right"] = ""; - $d["border_bottom"] = ""; - $d["border_left"] = ""; - $d["border_top_color"] = ""; - $d["border_right_color"] = ""; - $d["border_bottom_color"] = ""; - $d["border_left_color"] = ""; - $d["border_top_style"] = "none"; - $d["border_right_style"] = "none"; - $d["border_bottom_style"] = "none"; - $d["border_left_style"] = "none"; - $d["border_top_width"] = "medium"; - $d["border_right_width"] = "medium"; - $d["border_bottom_width"] = "medium"; - $d["border_left_width"] = "medium"; - $d["border_width"] = "medium"; - $d["border_bottom_left_radius"] = ""; - $d["border_bottom_right_radius"] = ""; - $d["border_top_left_radius"] = ""; - $d["border_top_right_radius"] = ""; - $d["border_radius"] = ""; - $d["border"] = ""; - $d["bottom"] = "auto"; - $d["caption_side"] = "top"; - $d["clear"] = "none"; - $d["clip"] = "auto"; - $d["color"] = "#000000"; - $d["content"] = "normal"; - $d["counter_increment"] = "none"; - $d["counter_reset"] = "none"; - $d["cue_after"] = "none"; - $d["cue_before"] = "none"; - $d["cue"] = ""; - $d["cursor"] = "auto"; - $d["direction"] = "ltr"; - $d["display"] = "inline"; - $d["elevation"] = "level"; - $d["empty_cells"] = "show"; - $d["float"] = "none"; - $d["font_family"] = $stylesheet->get_dompdf()->getOptions()->getDefaultFont(); - $d["font_size"] = "medium"; - $d["font_style"] = "normal"; - $d["font_variant"] = "normal"; - $d["font_weight"] = "normal"; - $d["font"] = ""; - $d["height"] = "auto"; - $d["image_resolution"] = "normal"; - $d["_dompdf_image_resolution"] = $d["image_resolution"]; - $d["_dompdf_keep"] = ""; - $d["left"] = "auto"; - $d["letter_spacing"] = "normal"; - $d["line_height"] = "normal"; - $d["list_style_image"] = "none"; - $d["list_style_position"] = "outside"; - $d["list_style_type"] = "disc"; - $d["list_style"] = ""; - $d["margin_right"] = "0"; - $d["margin_left"] = "0"; - $d["margin_top"] = "0"; - $d["margin_bottom"] = "0"; - $d["margin"] = ""; - $d["max_height"] = "none"; - $d["max_width"] = "none"; - $d["min_height"] = "0"; - $d["min_width"] = "0"; - $d["opacity"] = "1.0"; // CSS3 - $d["orphans"] = "2"; - $d["outline_color"] = ""; // "invert" special color is not supported - $d["outline_style"] = "none"; - $d["outline_width"] = "medium"; - $d["outline"] = ""; - $d["overflow"] = "visible"; - $d["padding_top"] = "0"; - $d["padding_right"] = "0"; - $d["padding_bottom"] = "0"; - $d["padding_left"] = "0"; - $d["padding"] = ""; - $d["page_break_after"] = "auto"; - $d["page_break_before"] = "auto"; - $d["page_break_inside"] = "auto"; - $d["pause_after"] = "0"; - $d["pause_before"] = "0"; - $d["pause"] = ""; - $d["pitch_range"] = "50"; - $d["pitch"] = "medium"; - $d["play_during"] = "auto"; - $d["position"] = "static"; - $d["quotes"] = ""; - $d["richness"] = "50"; - $d["right"] = "auto"; - $d["size"] = "auto"; // @page - $d["speak_header"] = "once"; - $d["speak_numeral"] = "continuous"; - $d["speak_punctuation"] = "none"; - $d["speak"] = "normal"; - $d["speech_rate"] = "medium"; - $d["stress"] = "50"; - $d["table_layout"] = "auto"; - $d["text_align"] = "left"; - $d["text_decoration"] = "none"; - $d["text_indent"] = "0"; - $d["text_transform"] = "none"; - $d["top"] = "auto"; - $d["transform"] = "none"; // CSS3 - $d["transform_origin"] = "50% 50%"; // CSS3 - $d["_webkit_transform"] = $d["transform"]; // CSS3 - $d["_webkit_transform_origin"] = $d["transform_origin"]; // CSS3 - $d["unicode_bidi"] = "normal"; - $d["vertical_align"] = "baseline"; - $d["visibility"] = "visible"; - $d["voice_family"] = ""; - $d["volume"] = "medium"; - $d["white_space"] = "normal"; - $d["word_wrap"] = "normal"; - $d["widows"] = "2"; - $d["width"] = "auto"; - $d["word_spacing"] = "normal"; - $d["z_index"] = "auto"; - - // for @font-face - $d["src"] = ""; - $d["unicode_range"] = ""; - - // Properties that inherit by default - self::$_inherited = array( - "azimuth", - "background_image_resolution", - "border_collapse", - "border_spacing", - "caption_side", - "color", - "cursor", - "direction", - "elevation", - "empty_cells", - "font_family", - "font_size", - "font_style", - "font_variant", - "font_weight", - "font", - "image_resolution", - "letter_spacing", - "line_height", - "list_style_image", - "list_style_position", - "list_style_type", - "list_style", - "orphans", - "page_break_inside", - "pitch_range", - "pitch", - "quotes", - "richness", - "speak_header", - "speak_numeral", - "speak_punctuation", - "speak", - "speech_rate", - "stress", - "text_align", - "text_indent", - "text_transform", - "visibility", - "voice_family", - "volume", - "white_space", - "word_wrap", - "widows", - "word_spacing", - ); - } - } - - /** - * "Destructor": forcibly free all references held by this object - */ - function dispose() - { - } - - /** - * @param $media_queries - */ - function set_media_queries($media_queries) - { - $this->_media_queries = $media_queries; - } - - /** - * @return array|int - */ - function get_media_queries() - { - return $this->_media_queries; - } - - /** - * @param Frame $frame - */ - function set_frame(Frame $frame) - { - $this->_frame = $frame; - } - - /** - * @return Frame - */ - function get_frame() - { - return $this->_frame; - } - - /** - * @param $origin - */ - function set_origin($origin) - { - $this->_origin = $origin; - } - - /** - * @return int - */ - function get_origin() - { - return $this->_origin; - } - - /** - * returns the {@link Stylesheet} this Style is associated with. - * - * @return Stylesheet - */ - function get_stylesheet() - { - return $this->_stylesheet; - } - - /** - * Converts any CSS length value into an absolute length in points. - * - * length_in_pt() takes a single length (e.g. '1em') or an array of - * lengths and returns an absolute length. If an array is passed, then - * the return value is the sum of all elements. If any of the lengths - * provided are "auto" or "none" then that value is returned. - * - * If a reference size is not provided, the default font size is used - * ({@link Style::$default_font_size}). - * - * @param float|string|array $length the numeric length (or string measurement) or array of lengths to resolve - * @param float $ref_size an absolute reference size to resolve percentage lengths - * @return float|string - */ - function length_in_pt($length, $ref_size = null) - { - static $cache = array(); - - if (!isset($ref_size)) { - $ref_size = self::$default_font_size; - } - - if (!is_array($length)) { - $key = $length . "/$ref_size"; - //Early check on cache, before converting $length to array - if (isset($cache[$key])) { - return $cache[$key]; - } - $length = array($length); - } else { - $key = implode("@", $length) . "/$ref_size"; - if (isset($cache[$key])) { - return $cache[$key]; - } - } - - $ret = 0; - foreach ($length as $l) { - - if ($l === "auto") { - return "auto"; - } - - if ($l === "none") { - return "none"; - } - - // Assume numeric values are already in points - if (is_numeric($l)) { - $ret += $l; - continue; - } - - if ($l === "normal") { - $ret += (float)$ref_size; - continue; - } - - // Border lengths - if ($l === "thin") { - $ret += 0.5; - continue; - } - - if ($l === "medium") { - $ret += 1.5; - continue; - } - - if ($l === "thick") { - $ret += 2.5; - continue; - } - - if (($i = mb_strpos($l, "px")) !== false) { - $dpi = $this->_stylesheet->get_dompdf()->getOptions()->getDpi(); - $ret += ((float)mb_substr($l, 0, $i) * 72) / $dpi; - continue; - } - - if (($i = mb_strpos($l, "pt")) !== false) { - $ret += (float)mb_substr($l, 0, $i); - continue; - } - - if (($i = mb_strpos($l, "%")) !== false) { - $ret += (float)mb_substr($l, 0, $i) / 100 * (float)$ref_size; - continue; - } - - if (($i = mb_strpos($l, "rem")) !== false) { - if ($this->_stylesheet->get_dompdf()->getTree()->get_root()->get_style() === null) { - // Interpreting it as "em", see https://github.com/dompdf/dompdf/issues/1406 - $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size"); - } else { - $ret += (float)mb_substr($l, 0, $i) * $this->_stylesheet->get_dompdf()->getTree()->get_root()->get_style()->font_size; - } - continue; - } - - if (($i = mb_strpos($l, "em")) !== false) { - $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size"); - continue; - } - - if (($i = mb_strpos($l, "cm")) !== false) { - $ret += (float)mb_substr($l, 0, $i) * 72 / 2.54; - continue; - } - - if (($i = mb_strpos($l, "mm")) !== false) { - $ret += (float)mb_substr($l, 0, $i) * 72 / 25.4; - continue; - } - - // FIXME: em:ex ratio? - if (($i = mb_strpos($l, "ex")) !== false) { - $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size") / 2; - continue; - } - - if (($i = mb_strpos($l, "in")) !== false) { - $ret += (float)mb_substr($l, 0, $i) * 72; - continue; - } - - if (($i = mb_strpos($l, "pc")) !== false) { - $ret += (float)mb_substr($l, 0, $i) * 12; - continue; - } - - // Bogus value - $ret += (float)$ref_size; - } - - return $cache[$key] = $ret; - } - - - /** - * Set inherited properties in this style using values in $parent - * - * @param Style $parent - * - * @return Style - */ - function inherit(Style $parent) - { - - // Set parent font size - $this->_parent_font_size = $parent->get_font_size(); - - foreach (self::$_inherited as $prop) { - //inherit the !important property also. - //if local property is also !important, don't inherit. - if (isset($parent->_props[$prop]) && - (!isset($this->_props[$prop]) || - (isset($parent->_important_props[$prop]) && !isset($this->_important_props[$prop])) - ) - ) { - if (isset($parent->_important_props[$prop])) { - $this->_important_props[$prop] = true; - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $parent->_props[$prop]; - } - } - - foreach ($this->_props as $prop => $value) { - if ($value === "inherit") { - if (isset($parent->_important_props[$prop])) { - $this->_important_props[$prop] = true; - } - //do not assign direct, but - //implicite assignment through __set, redirect to specialized, get value with __get - //This is for computing defaults if the parent setting is also missing. - //Therefore do not directly assign the value without __set - //set _important_props before that to be able to propagate. - //see __set and __get, on all assignments clear cache! - //$this->_prop_cache[$prop] = null; - //$this->_props[$prop] = $parent->_props[$prop]; - //props_set for more obvious explicite assignment not implemented, because - //too many implicite uses. - // $this->props_set($prop, $parent->$prop); - $this->__set($prop, $parent->__get($prop)); - } - } - - return $this; - } - - /** - * Override properties in this style with those in $style - * - * @param Style $style - */ - function merge(Style $style) - { - $shorthand_properties = array("background", "border", "border_bottom", "border_color", "border_left", "border_radius", "border_right", "border_style", "border_top", "border_width", "flex", "font", "list_style", "margin", "padding", "transform"); - //treat the !important attribute - //if old rule has !important attribute, override with new rule only if - //the new rule is also !important - foreach ($style->_props as $prop => $val) { - $can_merge = false; - if (isset($style->_important_props[$prop])) { - $this->_important_props[$prop] = true; - $can_merge = true; - } else if (!isset($this->_important_props[$prop])) { - $can_merge = true; - } - - if ($can_merge) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $val; - - // Clear out "inherit" shorthand properties if specific properties have been set - $shorthands = array_filter($shorthand_properties, function($el) use ($prop) { - return ( strpos($prop, $el."_") !== false ); - }); - foreach ($shorthands as $shorthand) { - if (array_key_exists($shorthand, $this->_props) && $this->_props[$shorthand] === "inherit") { - unset($this->_props[$shorthand]); - } - } - } - } - - if (isset($style->_props["font_size"])) { - $this->__font_size_calculated = false; - } - } - - /** - * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb") - * based on the provided CSS color value. - * - * @param string $color - * @return array - */ - function munge_color($color) - { - return Color::parse($color); - } - - /* direct access to _important_props array from outside would work only when declared as - * 'var $_important_props;' instead of 'protected $_important_props;' - * Don't call _set/__get on missing attribute. Therefore need a special access. - * Assume that __set will be also called when this is called, so do not check validity again. - * Only created, if !important exists -> always set true. - */ - function important_set($prop) - { - $prop = str_replace("-", "_", $prop); - $this->_important_props[$prop] = true; - } - - /** - * @param $prop - * @return bool - */ - function important_get($prop) - { - return isset($this->_important_props[$prop]); - } - - /** - * PHP5 overloaded setter - * - * This function along with {@link Style::__get()} permit a user of the - * Style class to access any (CSS) property using the following syntax: - * - * Style->margin_top = "1em"; - * echo (Style->margin_top); - * - * - * __set() automatically calls the provided set function, if one exists, - * otherwise it sets the property directly. Typically, __set() is not - * called directly from outside of this class. - * - * On each modification clear cache to return accurate setting. - * Also affects direct settings not using __set - * For easier finding all assignments, attempted to allowing only explicite assignment: - * Very many uses, e.g. AbstractFrameReflower.php -> for now leave as it is - * function __set($prop, $val) { - * throw new Exception("Implicit replacement of assignment by __set. Not good."); - * } - * function props_set($prop, $val) { ... } - * - * @param string $prop the property to set - * @param mixed $val the value of the property - * - */ - function __set($prop, $val) - { - $prop = str_replace("-", "_", $prop); - $this->_prop_cache[$prop] = null; - - if (!isset(self::$_defaults[$prop])) { - global $_dompdf_warnings; - $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property."; - return; - } - - if ($prop !== "content" && is_string($val) && strlen($val) > 5 && mb_strpos($val, "url") === false) { - $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val))); - $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val); - } - - $method = "set_$prop"; - - if (!isset(self::$_methods_cache[$method])) { - self::$_methods_cache[$method] = method_exists($this, $method); - } - - if (self::$_methods_cache[$method]) { - $this->$method($val); - } else { - $this->_props[$prop] = $val; - } - } - - /** - * PHP5 overloaded getter - * Along with {@link Style::__set()} __get() provides access to all CSS - * properties directly. Typically __get() is not called directly outside - * of this class. - * On each modification clear cache to return accurate setting. - * Also affects direct settings not using __set - * - * @param string $prop - * - * @throws Exception - * @return mixed - */ - function __get($prop) - { - if (!isset(self::$_defaults[$prop])) { - throw new Exception("'$prop' is not a valid CSS2 property."); - } - - if (isset($this->_prop_cache[$prop]) && $this->_prop_cache[$prop] != null) { - return $this->_prop_cache[$prop]; - } - - $method = "get_$prop"; - - // Fall back on defaults if property is not set - if (!isset($this->_props[$prop])) { - $this->_props[$prop] = self::$_defaults[$prop]; - } - - if (!isset(self::$_methods_cache[$method])) { - self::$_methods_cache[$method] = method_exists($this, $method); - } - - if (self::$_methods_cache[$method]) { - return $this->_prop_cache[$prop] = $this->$method(); - } - - return $this->_prop_cache[$prop] = $this->_props[$prop]; - } - - /** - * Similar to __get() without storing the result. Useful for accessing - * properties while loading stylesheets. - * - * @param $prop - * @return string - * @throws Exception - */ - function get_prop($prop) - { - if (!isset(self::$_defaults[$prop])) { - throw new Exception("'$prop' is not a valid CSS2 property."); - } - - $method = "get_$prop"; - - // Fall back on defaults if property is not set - if (!isset($this->_props[$prop])) { - return self::$_defaults[$prop]; - } - - if (method_exists($this, $method)) { - return $this->$method(); - } - - return $this->_props[$prop]; - } - - /** - * @return float|null|string - */ - function computed_bottom_spacing() { - if ($this->_computed_bottom_spacing !== null) { - return $this->_computed_bottom_spacing; - } - return $this->_computed_bottom_spacing = $this->length_in_pt( - array( - $this->margin_bottom, - $this->padding_bottom, - $this->border_bottom_width - ) - ); - } - - /** - * @return string - */ - function get_font_family_raw() - { - return trim($this->_props["font_family"], " \t\n\r\x0B\"'"); - } - - /** - * Getter for the 'font-family' CSS property. - * Uses the {@link FontMetrics} class to resolve the font family into an - * actual font file. - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family - * @throws Exception - * - * @return string - */ - function get_font_family() - { - if (isset($this->_font_family)) { - return $this->_font_family; - } - - $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss(); - - // Select the appropriate font. First determine the subtype, then check - // the specified font-families for a candidate. - - // Resolve font-weight - $weight = $this->__get("font_weight"); - - if (is_numeric($weight)) { - if ($weight < 600) { - $weight = "normal"; - } else { - $weight = "bold"; - } - } else if ($weight === "bold" || $weight === "bolder") { - $weight = "bold"; - } else { - $weight = "normal"; - } - - // Resolve font-style - $font_style = $this->__get("font_style"); - - if ($weight === "bold" && ($font_style === "italic" || $font_style === "oblique")) { - $subtype = "bold_italic"; - } else if ($weight === "bold" && $font_style !== "italic" && $font_style !== "oblique") { - $subtype = "bold"; - } else if ($weight !== "bold" && ($font_style === "italic" || $font_style === "oblique")) { - $subtype = "italic"; - } else { - $subtype = "normal"; - } - - // Resolve the font family - if ($DEBUGCSS) { - print "
[get_font_family:";
-            print '(' . $this->_props["font_family"] . '.' . $font_style . '.' . $this->__get("font_weight") . '.' . $weight . '.' . $subtype . ')';
-        }
-
-        $families = preg_split("/\s*,\s*/", $this->_props["font_family"]);
-
-        $font = null;
-        foreach ($families as $family) {
-            //remove leading and trailing string delimiters, e.g. on font names with spaces;
-            //remove leading and trailing whitespace
-            $family = trim($family, " \t\n\r\x0B\"'");
-            if ($DEBUGCSS) {
-                print '(' . $family . ')';
-            }
-            $font = $this->getFontMetrics()->getFont($family, $subtype);
-
-            if ($font) {
-                if ($DEBUGCSS) {
-                    print '(' . $font . ")get_font_family]\n
"; - } - return $this->_font_family = $font; - } - } - - $family = null; - if ($DEBUGCSS) { - print '(default)'; - } - $font = $this->getFontMetrics()->getFont($family, $subtype); - - if ($font) { - if ($DEBUGCSS) { - print '(' . $font . ")get_font_family]\n
"; - } - return $this->_font_family = $font; - } - - throw new Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] . "'"); - - } - - /** - * Returns the resolved font size, in points - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size - * @return float - */ - function get_font_size() - { - - if ($this->__font_size_calculated) { - return $this->_props["font_size"]; - } - - if (!isset($this->_props["font_size"])) { - $fs = self::$_defaults["font_size"]; - } else { - $fs = $this->_props["font_size"]; - } - - if (!isset($this->_parent_font_size)) { - $this->_parent_font_size = self::$default_font_size; - } - - switch ((string)$fs) { - case "xx-small": - case "x-small": - case "small": - case "medium": - case "large": - case "x-large": - case "xx-large": - $fs = self::$default_font_size * self::$font_size_keywords[$fs]; - break; - - case "smaller": - $fs = 8 / 9 * $this->_parent_font_size; - break; - - case "larger": - $fs = 6 / 5 * $this->_parent_font_size; - break; - - default: - break; - } - - // Ensure relative sizes resolve to something - if (($i = mb_strpos($fs, "em")) !== false) { - $fs = (float)mb_substr($fs, 0, $i) * $this->_parent_font_size; - } else if (($i = mb_strpos($fs, "ex")) !== false) { - $fs = (float)mb_substr($fs, 0, $i) * $this->_parent_font_size; - } else { - $fs = (float)$this->length_in_pt($fs); - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["font_size"] = null; - $this->_props["font_size"] = $fs; - $this->__font_size_calculated = true; - return $this->_props["font_size"]; - - } - - /** - * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing - * @return float - */ - function get_word_spacing() - { - if ($this->_props["word_spacing"] === "normal") { - return 0; - } - - return $this->_props["word_spacing"]; - } - - /** - * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing - * @return float - */ - function get_letter_spacing() - { - if ($this->_props["letter_spacing"] === "normal") { - return 0; - } - - return $this->_props["letter_spacing"]; - } - - /** - * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height - * @return float - */ - function get_line_height() - { - if (array_key_exists("line_height", $this->_props) === false) { - $this->_props["line_height"] = self::$_defaults["line_height"]; - } - $line_height = $this->_props["line_height"]; - - if ($line_height === "normal") { - return self::$default_line_height * $this->get_font_size(); - } - - if (is_numeric($line_height)) { - return $this->length_in_pt($line_height . "em", $this->get_font_size()); - } - - return $this->length_in_pt($line_height, $this->_parent_font_size); - } - - /** - * Returns the color as an array - * - * The array has the following format: - * array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb") - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color - * @return array - */ - function get_color() - { - return $this->munge_color($this->_props["color"]); - } - - /** - * Returns the background color as an array - * - * The returned array has the same format as {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color - * @return array - */ - function get_background_color() - { - return $this->munge_color($this->_props["background_color"]); - } - - /** - * Returns the background position as an array - * - * The returned array has the following format: - * array(x,y, "x" => x, "y" => y) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position - * @return array - */ - function get_background_position() - { - $tmp = explode(" ", $this->_props["background_position"]); - - switch ($tmp[0]) { - case "left": - $x = "0%"; - break; - - case "right": - $x = "100%"; - break; - - case "top": - $y = "0%"; - break; - - case "bottom": - $y = "100%"; - break; - - case "center": - $x = "50%"; - $y = "50%"; - break; - - default: - $x = $tmp[0]; - break; - } - - if (isset($tmp[1])) { - switch ($tmp[1]) { - case "left": - $x = "0%"; - break; - - case "right": - $x = "100%"; - break; - - case "top": - $y = "0%"; - break; - - case "bottom": - $y = "100%"; - break; - - case "center": - if ($tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center") { - $y = "50%"; - } else { - $x = "50%"; - } - break; - - default: - $y = $tmp[1]; - break; - } - } else { - $y = "50%"; - } - - if (!isset($x)) { - $x = "0%"; - } - - if (!isset($y)) { - $y = "0%"; - } - - return array( - 0 => $x, "x" => $x, - 1 => $y, "y" => $y, - ); - } - - - /** - * Returns the background as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment - * @return string - */ - function get_background_attachment() - { - return $this->_props["background_attachment"]; - } - - - /** - * Returns the background_repeat as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat - * @return string - */ - function get_background_repeat() - { - return $this->_props["background_repeat"]; - } - - - /** - * Returns the background as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing, but the individual get_background_xxx) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background - * @return string - */ - function get_background() - { - return $this->_props["background"]; - } - - - /**#@+ - * Returns the border color as an array - * - * See {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties - * @return array - */ - function get_border_top_color() - { - if ($this->_props["border_top_color"] === "") { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_top_color"] = null; - $this->_props["border_top_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_top_color"]); - } - - /** - * @return array - */ - function get_border_right_color() - { - if ($this->_props["border_right_color"] === "") { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_right_color"] = null; - $this->_props["border_right_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_right_color"]); - } - - /** - * @return array - */ - function get_border_bottom_color() - { - if ($this->_props["border_bottom_color"] === "") { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_bottom_color"] = null; - $this->_props["border_bottom_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_bottom_color"]); - } - - /** - * @return array - */ - function get_border_left_color() - { - if ($this->_props["border_left_color"] === "") { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_left_color"] = null; - $this->_props["border_left_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_left_color"]); - } - - /**#@-*/ - - /**#@+ - * Returns the border width, as it is currently stored - * - * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties - * @return float|string - */ - function get_border_top_width() - { - $style = $this->__get("border_top_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0; - } - - /** - * @return float|int|string - */ - function get_border_right_width() - { - $style = $this->__get("border_right_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0; - } - - /** - * @return float|int|string - */ - function get_border_bottom_width() - { - $style = $this->__get("border_bottom_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0; - } - - /** - * @return float|int|string - */ - function get_border_left_width() - { - $style = $this->__get("border_left_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0; - } - /**#@-*/ - - /** - * Return an array of all border properties. - * - * The returned array has the following structure: - * - * array("top" => array("width" => [border-width], - * "style" => [border-style], - * "color" => [border-color (array)]), - * "bottom" ... ) - * - * - * @return array - */ - function get_border_properties() - { - return array( - "top" => array( - "width" => $this->__get("border_top_width"), - "style" => $this->__get("border_top_style"), - "color" => $this->__get("border_top_color"), - ), - "bottom" => array( - "width" => $this->__get("border_bottom_width"), - "style" => $this->__get("border_bottom_style"), - "color" => $this->__get("border_bottom_color"), - ), - "right" => array( - "width" => $this->__get("border_right_width"), - "style" => $this->__get("border_right_style"), - "color" => $this->__get("border_right_color"), - ), - "left" => array( - "width" => $this->__get("border_left_width"), - "style" => $this->__get("border_left_style"), - "color" => $this->__get("border_left_color"), - ), - ); - } - - /** - * Return a single border property - * - * @param string $side - * - * @return mixed - */ - protected function _get_border($side) - { - $color = $this->__get("border_" . $side . "_color"); - - return $this->__get("border_" . $side . "_width") . " " . - $this->__get("border_" . $side . "_style") . " " . $color["hex"]; - } - - /**#@+ - * Return full border properties as a string - * - * Border properties are returned just as specified in CSS: - *
[width] [style] [color]
- * e.g. "1px solid blue" - * - * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties - * @return string - */ - function get_border_top() - { - return $this->_get_border("top"); - } - - /** - * @return mixed - */ - function get_border_right() - { - return $this->_get_border("right"); - } - - /** - * @return mixed - */ - function get_border_bottom() - { - return $this->_get_border("bottom"); - } - - /** - * @return mixed - */ - function get_border_left() - { - return $this->_get_border("left"); - } - - /** - * @param $w - * @param $h - * @return array|null - */ - function get_computed_border_radius($w, $h) - { - if (!empty($this->_computed_border_radius)) { - return $this->_computed_border_radius; - } - - $w = (float)$w; - $h = (float)$h; - $rTL = (float)$this->__get("border_top_left_radius"); - $rTR = (float)$this->__get("border_top_right_radius"); - $rBL = (float)$this->__get("border_bottom_left_radius"); - $rBR = (float)$this->__get("border_bottom_right_radius"); - - if ($rTL + $rTR + $rBL + $rBR == 0) { - return $this->_computed_border_radius = array( - 0, 0, 0, 0, - "top-left" => 0, - "top-right" => 0, - "bottom-right" => 0, - "bottom-left" => 0, - ); - } - - $t = (float)$this->__get("border_top_width"); - $r = (float)$this->__get("border_right_width"); - $b = (float)$this->__get("border_bottom_width"); - $l = (float)$this->__get("border_left_width"); - - $rTL = min($rTL, $h - $rBL - $t / 2 - $b / 2, $w - $rTR - $l / 2 - $r / 2); - $rTR = min($rTR, $h - $rBR - $t / 2 - $b / 2, $w - $rTL - $l / 2 - $r / 2); - $rBL = min($rBL, $h - $rTL - $t / 2 - $b / 2, $w - $rBR - $l / 2 - $r / 2); - $rBR = min($rBR, $h - $rTR - $t / 2 - $b / 2, $w - $rBL - $l / 2 - $r / 2); - - return $this->_computed_border_radius = array( - $rTL, $rTR, $rBR, $rBL, - "top-left" => $rTL, - "top-right" => $rTR, - "bottom-right" => $rBR, - "bottom-left" => $rBL, - ); - } - - /** - * Returns the outline color as an array - * - * See {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties - * @return array - */ - function get_outline_color() - { - if ($this->_props["outline_color"] === "") { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["outline_color"] = null; - $this->_props["outline_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["outline_color"]); - } - - /**#@+ - * Returns the outline width, as it is currently stored - * @return float|string - */ - function get_outline_width() - { - $style = $this->__get("outline_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["outline_width"]) : 0; - } - - /**#@+ - * Return full outline properties as a string - * - * Outline properties are returned just as specified in CSS: - *
[width] [style] [color]
- * e.g. "1px solid blue" - * - * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties - * @return string - */ - function get_outline() - { - $color = $this->__get("outline_color"); - return - $this->__get("outline_width") . " " . - $this->__get("outline_style") . " " . - $color["hex"]; - } - /**#@-*/ - - /** - * Returns border spacing as an array - * - * The array has the format (h_space,v_space) - * - * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing - * @return array - */ - function get_border_spacing() - { - $arr = explode(" ", $this->_props["border_spacing"]); - if (count($arr) == 1) { - $arr[1] = $arr[0]; - } - return $arr; - } - - /*==============================*/ - - /* - !important attribute - For basic functionality of the !important attribute with overloading - of several styles of an element, changes in inherit(), merge() and _parse_properties() - are sufficient [helpers var $_important_props, __construct(), important_set(), important_get()] - - Only for combined attributes extra treatment needed. See below. - - div { border: 1px red; } - div { border: solid; } // Not combined! Only one occurrence of same style per context - // - div { border: 1px red; } - div a { border: solid; } // Adding to border style ok by inheritance - // - div { border-style: solid; } // Adding to border style ok because of different styles - div { border: 1px red; } - // - div { border-style: solid; !important} // border: overrides, even though not !important - div { border: 1px dashed red; } - // - div { border: 1px red; !important } - div a { border-style: solid; } // Need to override because not set - - Special treatment: - At individual property like border-top-width need to check whether overriding value is also !important. - Also store the !important condition for later overrides. - Since not known who is initiating the override, need to get passed !important as parameter. - !important Parameter taken as in the original style in the css file. - When property border !important given, do not mark subsets like border_style as important. Only - individual properties. - - Note: - Setting individual property directly from css with e.g. set_border_top_style() is not needed, because - missing set functions handled by a generic handler __set(), including the !important. - Setting individual property of as sub-property is handled below. - - Implementation see at _set_style_side_type() - Callers _set_style_sides_type(), _set_style_type, _set_style_type_important() - - Related functionality for background, padding, margin, font, list_style - */ - - /** - * Generalized set function for individual attribute of combined style. - * With check for !important - * Applicable for background, border, padding, margin, font, list_style - * - * Note: $type has a leading underscore (or is empty), the others not. - * - * @param $style - * @param $side - * @param $type - * @param $val - * @param $important - */ - protected function _set_style_side_type($style, $side, $type, $val, $important) - { - $prop = $style . '_' . $side . $type; - - if (!isset($this->_important_props[$prop]) || $important) { - if ($side === "bottom") { - $this->_computed_bottom_spacing = null; //reset computed cache, border style can disable/enable border calculations - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - if ($important) { - $this->_important_props[$prop] = true; - } - $this->_props[$prop] = $val; - } - } - - /** - * @param $style - * @param $top - * @param $right - * @param $bottom - * @param $left - * @param $type - * @param $important - */ - protected function _set_style_sides_type($style, $top, $right, $bottom, $left, $type, $important) - { - $this->_set_style_side_type($style, 'top', $type, $top, $important); - $this->_set_style_side_type($style, 'right', $type, $right, $important); - $this->_set_style_side_type($style, 'bottom', $type, $bottom, $important); - $this->_set_style_side_type($style, 'left', $type, $left, $important); - } - - /** - * @param $style - * @param $type - * @param $val - * @param $important - */ - protected function _set_style_type($style, $type, $val, $important) - { - $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $arr = explode(" ", $val); - - switch (count($arr)) { - case 1: - $this->_set_style_sides_type($style, $arr[0], $arr[0], $arr[0], $arr[0], $type, $important); - break; - case 2: - $this->_set_style_sides_type($style, $arr[0], $arr[1], $arr[0], $arr[1], $type, $important); - break; - case 3: - $this->_set_style_sides_type($style, $arr[0], $arr[1], $arr[2], $arr[1], $type, $important); - break; - case 4: - $this->_set_style_sides_type($style, $arr[0], $arr[1], $arr[2], $arr[3], $type, $important); - break; - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style . $type] = null; - $this->_props[$style . $type] = $val; - } - - /** - * @param $style - * @param $type - * @param $val - */ - protected function _set_style_type_important($style, $type, $val) - { - $this->_set_style_type($style, $type, $val, isset($this->_important_props[$style . $type])); - } - - /** - * Anyway only called if _important matches and is assigned - * E.g. _set_style_side_type($style,$side,'',str_replace("none", "0px", $val),isset($this->_important_props[$style.'_'.$side])); - * - * @param $style - * @param $side - * @param $val - */ - protected function _set_style_side_width_important($style, $side, $val) - { - if ($side === "bottom") { - $this->_computed_bottom_spacing = null; //reset cache for any bottom width changes - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style . '_' . $side] = null; - $this->_props[$style . '_' . $side] = str_replace("none", "0px", $val); - } - - /** - * @param $style - * @param $val - * @param $important - */ - protected function _set_style($style, $val, $important) - { - if (!isset($this->_important_props[$style]) || $important) { - if ($important) { - $this->_important_props[$style] = true; - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style] = null; - $this->_props[$style] = $val; - } - } - - /** - * @param $val - * @return string - */ - protected function _image($val) - { - $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss(); - $parsed_url = "none"; - - if (mb_strpos($val, "url") === false) { - $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none - } else { - $val = preg_replace("/url\(\s*['\"]?([^'\")]+)['\"]?\s*\)/", "\\1", trim($val)); - - // Resolve the url now in the context of the current stylesheet - $parsed_url = Helpers::explode_url($val); - if ($parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "") { - if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\') { - $path = $_SERVER["DOCUMENT_ROOT"] . '/'; - } else { - $path = $this->_stylesheet->get_base_path(); - } - - $path .= $parsed_url["path"] . $parsed_url["file"]; - $path = realpath($path); - // If realpath returns FALSE then specifically state that there is no background image - if (!$path) { - $path = 'none'; - } - } else { - $path = Helpers::build_url($this->_stylesheet->get_protocol(), - $this->_stylesheet->get_host(), - $this->_stylesheet->get_base_path(), - $val); - } - } - if ($DEBUGCSS) { - print "
[_image\n";
-            print_r($parsed_url);
-            print $this->_stylesheet->get_protocol() . "\n" . $this->_stylesheet->get_base_path() . "\n" . $path . "\n";
-            print "_image]
";; - } - return $path; - } - - /*======================*/ - - /** - * Sets color - * - * The color parameter can be any valid CSS color value - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color - * @param string $color - */ - function set_color($color) - { - $col = $this->munge_color($color); - - if (is_null($col) || !isset($col["hex"])) { - $color = "inherit"; - } else { - $color = $col["hex"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["color"] = null; - $this->_props["color"] = $color; - } - - /** - * Sets the background color - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color - * @param string $color - */ - function set_background_color($color) - { - $col = $this->munge_color($color); - - if (is_null($col)) { - return; - //$col = self::$_defaults["background_color"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_color"] = null; - $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col; - } - - /** - * Set the background image url - * @link http://www.w3.org/TR/CSS21/colors.html#background-properties - * - * @param string $val - */ - function set_background_image($val) - { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_image"] = null; - $this->_props["background_image"] = $this->_image($val); - } - - /** - * Sets the background repeat - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat - * @param string $val - */ - function set_background_repeat($val) - { - if (is_null($val)) { - $val = self::$_defaults["background_repeat"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_repeat"] = null; - $this->_props["background_repeat"] = $val; - } - - /** - * Sets the background attachment - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment - * @param string $val - */ - function set_background_attachment($val) - { - if (is_null($val)) { - $val = self::$_defaults["background_attachment"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_attachment"] = null; - $this->_props["background_attachment"] = $val; - } - - /** - * Sets the background position - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position - * @param string $val - */ - function set_background_position($val) - { - if (is_null($val)) { - $val = self::$_defaults["background_position"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_position"] = null; - $this->_props["background_position"] = $val; - } - - /** - * Sets the background - combined options - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background - * @param string $val - */ - function set_background($val) - { - $val = trim($val); - $important = isset($this->_important_props["background"]); - - if ($val === "none") { - $this->_set_style("background_image", "none", $important); - $this->_set_style("background_color", "transparent", $important); - } else { - $pos = array(); - $tmp = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $tmp = preg_split("/\s+/", $tmp); - - foreach ($tmp as $attr) { - if (mb_substr($attr, 0, 3) === "url" || $attr === "none") { - $this->_set_style("background_image", $this->_image($attr), $important); - } elseif ($attr === "fixed" || $attr === "scroll") { - $this->_set_style("background_attachment", $attr, $important); - } elseif ($attr === "repeat" || $attr === "repeat-x" || $attr === "repeat-y" || $attr === "no-repeat") { - $this->_set_style("background_repeat", $attr, $important); - } elseif (($col = $this->munge_color($attr)) != null) { - $this->_set_style("background_color", is_array($col) ? $col["hex"] : $col, $important); - } else { - $pos[] = $attr; - } - } - - if (count($pos)) { - $this->_set_style("background_position", implode(" ", $pos), $important); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background"] = null; - $this->_props["background"] = $val; - } - - /** - * Sets the font size - * - * $size can be any acceptable CSS size - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size - * @param string|float $size - */ - function set_font_size($size) - { - $this->__font_size_calculated = false; - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["font_size"] = null; - $this->_props["font_size"] = $size; - } - - /** - * Sets the font style - * - * combined attributes - * set individual attributes also, respecting !important mark - * exactly this order, separate by space. Multiple fonts separated by comma: - * font-style, font-variant, font-weight, font-size, line-height, font-family - * - * Other than with border and list, existing partial attributes should - * reset when starting here, even when not mentioned. - * If individual attribute is !important and explicit or implicit replacement is not, - * keep individual attribute - * - * require whitespace as delimiters for single value attributes - * On delimiter "/" treat first as font height, second as line height - * treat all remaining at the end of line as font - * font-style, font-variant, font-weight, font-size, line-height, font-family - * - * missing font-size and font-family might be not allowed, but accept it here and - * use default (medium size, empty font name) - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style - * @param $val - */ - function set_font($val) - { - $this->__font_size_calculated = false; - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["font"] = null; - $this->_props["font"] = $val; - - $important = isset($this->_important_props["font"]); - - if (preg_match("/^(italic|oblique|normal)\s*(.*)$/i", $val, $match)) { - $this->_set_style("font_style", $match[1], $important); - $val = $match[2]; - } else { - $this->_set_style("font_style", self::$_defaults["font_style"], $important); - } - - if (preg_match("/^(small-caps|normal)\s*(.*)$/i", $val, $match)) { - $this->_set_style("font_variant", $match[1], $important); - $val = $match[2]; - } else { - $this->_set_style("font_variant", self::$_defaults["font_variant"], $important); - } - - //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip! - if (preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) && - !preg_match("/^(?:pt|px|pc|em|ex|in|cm|mm|%)/", $match[2]) - ) { - $this->_set_style("font_weight", $match[1], $important); - $val = $match[2]; - } else { - $this->_set_style("font_weight", self::$_defaults["font_weight"], $important); - } - - if (preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))(?:\/|\s*)(.*)$/i", $val, $match)) { - $this->_set_style("font_size", $match[1], $important); - $val = $match[2]; - if (preg_match("/^(?:\/|\s*)(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%)?)\s*(.*)$/i", $val, $match)) { - $this->_set_style("line_height", $match[1], $important); - $val = $match[2]; - } else { - $this->_set_style("line_height", self::$_defaults["line_height"], $important); - } - } else { - $this->_set_style("font_size", self::$_defaults["font_size"], $important); - $this->_set_style("line_height", self::$_defaults["line_height"], $important); - } - - if (strlen($val) != 0) { - $this->_set_style("font_family", $val, $important); - } else { - $this->_set_style("font_family", self::$_defaults["font_family"], $important); - } - } - - /** - * Sets page break properties - * - * @link http://www.w3.org/TR/CSS21/page.html#page-breaks - * @param string $break - */ - function set_page_break_before($break) - { - if ($break === "left" || $break === "right") { - $break = "always"; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["page_break_before"] = null; - $this->_props["page_break_before"] = $break; - } - - /** - * @param $break - */ - function set_page_break_after($break) - { - if ($break === "left" || $break === "right") { - $break = "always"; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["page_break_after"] = null; - $this->_props["page_break_after"] = $break; - } - - /** - * Sets the margin size - * - * @link http://www.w3.org/TR/CSS21/box.html#margin-properties - * @param $val - */ - function set_margin_top($val) - { - $this->_set_style_side_width_important('margin', 'top', $val); - } - - /** - * @param $val - */ - function set_margin_right($val) - { - $this->_set_style_side_width_important('margin', 'right', $val); - } - - /** - * @param $val - */ - function set_margin_bottom($val) - { - $this->_set_style_side_width_important('margin', 'bottom', $val); - } - - /** - * @param $val - */ - function set_margin_left($val) - { - $this->_set_style_side_width_important('margin', 'left', $val); - } - - /** - * @param $val - */ - function set_margin($val) - { - $val = str_replace("none", "0px", $val); - $this->_set_style_type_important('margin', '', $val); - } - - /** - * Sets the padding size - * - * @link http://www.w3.org/TR/CSS21/box.html#padding-properties - * @param $val - */ - function set_padding_top($val) - { - $this->_set_style_side_width_important('padding', 'top', $val); - } - - /** - * @param $val - */ - function set_padding_right($val) - { - $this->_set_style_side_width_important('padding', 'right', $val); - } - - /** - * @param $val - */ - function set_padding_bottom($val) - { - $this->_set_style_side_width_important('padding', 'bottom', $val); - } - - /** - * @param $val - */ - function set_padding_left($val) - { - $this->_set_style_side_width_important('padding', 'left', $val); - } - - /** - * @param $val - */ - function set_padding($val) - { - $val = str_replace("none", "0px", $val); - $this->_set_style_type_important('padding', '', $val); - } - /**#@-*/ - - /** - * Sets a single border - * - * @param string $side - * @param string $border_spec ([width] [style] [color]) - * @param boolean $important - */ - protected function _set_border($side, $border_spec, $important) - { - $border_spec = preg_replace("/\s*\,\s*/", ",", $border_spec); - //$border_spec = str_replace(",", " ", $border_spec); // Why did we have this ?? rbg(10, 102, 10) > rgb(10 102 10) - $arr = explode(" ", $border_spec); - - // FIXME: handle partial values - - //For consistency of individual and combined properties, and with ie8 and firefox3 - //reset all attributes, even if only partially given - $this->_set_style_side_type('border', $side, '_style', self::$_defaults['border_' . $side . '_style'], $important); - $this->_set_style_side_type('border', $side, '_width', self::$_defaults['border_' . $side . '_width'], $important); - $this->_set_style_side_type('border', $side, '_color', self::$_defaults['border_' . $side . '_color'], $important); - - foreach ($arr as $value) { - $value = trim($value); - if (in_array($value, self::$BORDER_STYLES)) { - $this->_set_style_side_type('border', $side, '_style', $value, $important); - } else if (preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value)) { - $this->_set_style_side_type('border', $side, '_width', $value, $important); - } else { - // must be color - $this->_set_style_side_type('border', $side, '_color', $value, $important); - } - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache['border_' . $side] = null; - $this->_props['border_' . $side] = $border_spec; - } - - /** - * Sets the border styles - * - * @link http://www.w3.org/TR/CSS21/box.html#border-properties - * @param string $val - */ - function set_border_top($val) - { - $this->_set_border("top", $val, isset($this->_important_props['border_top'])); - } - - /** - * @param $val - */ - function set_border_right($val) - { - $this->_set_border("right", $val, isset($this->_important_props['border_right'])); - } - - /** - * @param $val - */ - function set_border_bottom($val) - { - $this->_set_border("bottom", $val, isset($this->_important_props['border_bottom'])); - } - - /** - * @param $val - */ - function set_border_left($val) - { - $this->_set_border("left", $val, isset($this->_important_props['border_left'])); - } - - /** - * @param $val - */ - function set_border($val) - { - $important = isset($this->_important_props["border"]); - $this->_set_border("top", $val, $important); - $this->_set_border("right", $val, $important); - $this->_set_border("bottom", $val, $important); - $this->_set_border("left", $val, $important); - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["border"] = null; - $this->_props["border"] = $val; - } - - /** - * @param $val - */ - function set_border_width($val) - { - $this->_set_style_type_important('border', '_width', $val); - } - - /** - * @param $val - */ - function set_border_color($val) - { - $this->_set_style_type_important('border', '_color', $val); - } - - /** - * @param $val - */ - function set_border_style($val) - { - $this->_set_style_type_important('border', '_style', $val); - } - - /** - * Sets the border radius size - * - * http://www.w3.org/TR/css3-background/#corners - * - * @param $val - */ - function set_border_top_left_radius($val) - { - $this->_set_border_radius_corner($val, "top_left"); - } - - /** - * @param $val - */ - function set_border_top_right_radius($val) - { - $this->_set_border_radius_corner($val, "top_right"); - } - - /** - * @param $val - */ - function set_border_bottom_left_radius($val) - { - $this->_set_border_radius_corner($val, "bottom_left"); - } - - /** - * @param $val - */ - function set_border_bottom_right_radius($val) - { - $this->_set_border_radius_corner($val, "bottom_right"); - } - - /** - * @param $val - */ - function set_border_radius($val) - { - $val = preg_replace("/\s*\,\s*/", ",", $val); // when border-radius has spaces - $arr = explode(" ", $val); - - switch (count($arr)) { - case 1: - $this->_set_border_radii($arr[0], $arr[0], $arr[0], $arr[0]); - break; - case 2: - $this->_set_border_radii($arr[0], $arr[1], $arr[0], $arr[1]); - break; - case 3: - $this->_set_border_radii($arr[0], $arr[1], $arr[2], $arr[1]); - break; - case 4: - $this->_set_border_radii($arr[0], $arr[1], $arr[2], $arr[3]); - break; - } - } - - /** - * @param $val1 - * @param $val2 - * @param $val3 - * @param $val4 - */ - protected function _set_border_radii($val1, $val2, $val3, $val4) - { - $this->_set_border_radius_corner($val1, "top_left"); - $this->_set_border_radius_corner($val2, "top_right"); - $this->_set_border_radius_corner($val3, "bottom_right"); - $this->_set_border_radius_corner($val4, "bottom_left"); - } - - /** - * @param $val - * @param $corner - */ - protected function _set_border_radius_corner($val, $corner) - { - $this->_has_border_radius = true; - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_" . $corner . "_radius"] = null; - - $this->_props["border_" . $corner . "_radius"] = $val; - } - - /** - * @return float|int|string - */ - function get_border_top_left_radius() - { - return $this->_get_border_radius_corner("top_left"); - } - - /** - * @return float|int|string - */ - function get_border_top_right_radius() - { - return $this->_get_border_radius_corner("top_right"); - } - - /** - * @return float|int|string - */ - function get_border_bottom_left_radius() - { - return $this->_get_border_radius_corner("bottom_left"); - } - - /** - * @return float|int|string - */ - function get_border_bottom_right_radius() - { - return $this->_get_border_radius_corner("bottom_right"); - } - - /** - * @param $corner - * @return float|int|string - */ - protected function _get_border_radius_corner($corner) - { - if (!isset($this->_props["border_" . $corner . "_radius"]) || empty($this->_props["border_" . $corner . "_radius"])) { - return 0; - } - - return $this->length_in_pt($this->_props["border_" . $corner . "_radius"]); - } - - /** - * Sets the outline styles - * - * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines - * @param string $val - */ - function set_outline($val) - { - $important = isset($this->_important_props["outline"]); - - $props = array( - "outline_style", - "outline_width", - "outline_color", - ); - - foreach ($props as $prop) { - $_val = self::$_defaults[$prop]; - - if (!isset($this->_important_props[$prop]) || $important) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - if ($important) { - $this->_important_props[$prop] = true; - } - $this->_props[$prop] = $_val; - } - } - - $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $arr = explode(" ", $val); - foreach ($arr as $value) { - $value = trim($value); - - if (in_array($value, self::$BORDER_STYLES)) { - $this->set_outline_style($value); - } else if (preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value)) { - $this->set_outline_width($value); - } else { - // must be color - $this->set_outline_color($value); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["outline"] = null; - $this->_props["outline"] = $val; - } - - /** - * @param $val - */ - function set_outline_width($val) - { - $this->_set_style_type_important('outline', '_width', $val); - } - - /** - * @param $val - */ - function set_outline_color($val) - { - $this->_set_style_type_important('outline', '_color', $val); - } - - /** - * @param $val - */ - function set_outline_style($val) - { - $this->_set_style_type_important('outline', '_style', $val); - } - - /** - * Sets the border spacing - * - * @link http://www.w3.org/TR/CSS21/box.html#border-properties - * @param float $val - */ - function set_border_spacing($val) - { - $arr = explode(" ", $val); - - if (count($arr) == 1) { - $arr[1] = $arr[0]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["border_spacing"] = null; - $this->_props["border_spacing"] = "$arr[0] $arr[1]"; - } - - /** - * Sets the list style image - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image - * @param $val - */ - function set_list_style_image($val) - { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["list_style_image"] = null; - $this->_props["list_style_image"] = $this->_image($val); - } - - /** - * Sets the list style - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style - * @param $val - */ - function set_list_style($val) - { - $important = isset($this->_important_props["list_style"]); - $arr = explode(" ", str_replace(",", " ", $val)); - - static $types = array( - "disc", "circle", "square", - "decimal-leading-zero", "decimal", "1", - "lower-roman", "upper-roman", "a", "A", - "lower-greek", - "lower-latin", "upper-latin", - "lower-alpha", "upper-alpha", - "armenian", "georgian", "hebrew", - "cjk-ideographic", "hiragana", "katakana", - "hiragana-iroha", "katakana-iroha", "none" - ); - - static $positions = array("inside", "outside"); - - foreach ($arr as $value) { - /* http://www.w3.org/TR/CSS21/generate.html#list-style - * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none' - */ - if ($value === "none") { - $this->_set_style("list_style_type", $value, $important); - $this->_set_style("list_style_image", $value, $important); - continue; - } - - //On setting or merging or inheriting list_style_image as well as list_style_type, - //and url exists, then url has precedence, otherwise fall back to list_style_type - //Firefox is wrong here (list_style_image gets overwritten on explicit list_style_type) - //Internet Explorer 7/8 and dompdf is right. - - if (mb_substr($value, 0, 3) === "url") { - $this->_set_style("list_style_image", $this->_image($value), $important); - continue; - } - - if (in_array($value, $types)) { - $this->_set_style("list_style_type", $value, $important); - } else if (in_array($value, $positions)) { - $this->_set_style("list_style_position", $value, $important); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["list_style"] = null; - $this->_props["list_style"] = $val; - } - - /** - * @param $val - */ - function set_size($val) - { - $length_re = "/(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))/"; - - $val = mb_strtolower($val); - - if ($val === "auto") { - return; - } - - $parts = preg_split("/\s+/", $val); - - $computed = array(); - if (preg_match($length_re, $parts[0])) { - $computed[] = $this->length_in_pt($parts[0]); - - if (isset($parts[1]) && preg_match($length_re, $parts[1])) { - $computed[] = $this->length_in_pt($parts[1]); - } else { - $computed[] = $computed[0]; - } - - if (isset($parts[2]) && $parts[2] === "landscape") { - $computed = array_reverse($computed); - } - } elseif (isset(CPDF::$PAPER_SIZES[$parts[0]])) { - $computed = array_slice(CPDF::$PAPER_SIZES[$parts[0]], 2, 2); - - if (isset($parts[1]) && $parts[1] === "landscape") { - $computed = array_reverse($computed); - } - } else { - return; - } - - $this->_props["size"] = $computed; - } - - /** - * Gets the CSS3 transform property - * - * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property - * @return array|null - */ - function get_transform() - { - $number = "\s*([^,\s]+)\s*"; - $tr_value = "\s*([^,\s]+)\s*"; - $angle = "\s*([^,\s]+(?:deg|rad)?)\s*"; - - if (!preg_match_all("/[a-z]+\([^\)]+\)/i", $this->_props["transform"], $parts, PREG_SET_ORDER)) { - return null; - } - - $functions = array( - //"matrix" => "\($number,$number,$number,$number,$number,$number\)", - - "translate" => "\($tr_value(?:,$tr_value)?\)", - "translateX" => "\($tr_value\)", - "translateY" => "\($tr_value\)", - - "scale" => "\($number(?:,$number)?\)", - "scaleX" => "\($number\)", - "scaleY" => "\($number\)", - - "rotate" => "\($angle\)", - - "skew" => "\($angle(?:,$angle)?\)", - "skewX" => "\($angle\)", - "skewY" => "\($angle\)", - ); - - $transforms = array(); - - foreach ($parts as $part) { - $t = $part[0]; - - foreach ($functions as $name => $pattern) { - if (preg_match("/$name\s*$pattern/i", $t, $matches)) { - $values = array_slice($matches, 1); - - switch ($name) { - // units - case "rotate": - case "skew": - case "skewX": - case "skewY": - - foreach ($values as $i => $value) { - if (strpos($value, "rad")) { - $values[$i] = rad2deg(floatval($value)); - } else { - $values[$i] = floatval($value); - } - } - - switch ($name) { - case "skew": - if (!isset($values[1])) { - $values[1] = 0; - } - break; - case "skewX": - $name = "skew"; - $values = array($values[0], 0); - break; - case "skewY": - $name = "skew"; - $values = array(0, $values[0]); - break; - } - break; - - // units - case "translate": - $values[0] = $this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)); - - if (isset($values[1])) { - $values[1] = $this->length_in_pt($values[1], (float)$this->length_in_pt($this->height)); - } else { - $values[1] = 0; - } - break; - - case "translateX": - $name = "translate"; - $values = array($this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)), 0); - break; - - case "translateY": - $name = "translate"; - $values = array(0, $this->length_in_pt($values[0], (float)$this->length_in_pt($this->height))); - break; - - // units - case "scale": - if (!isset($values[1])) { - $values[1] = $values[0]; - } - break; - - case "scaleX": - $name = "scale"; - $values = array($values[0], 1.0); - break; - - case "scaleY": - $name = "scale"; - $values = array(1.0, $values[0]); - break; - } - - $transforms[] = array( - $name, - $values, - ); - } - } - } - - return $transforms; - } - - /** - * @param $val - */ - function set_transform($val) - { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["transform"] = null; - $this->_props["transform"] = $val; - } - - /** - * @param $val - */ - function set__webkit_transform($val) - { - $this->set_transform($val); - } - - /** - * @param $val - */ - function set__webkit_transform_origin($val) - { - $this->set_transform_origin($val); - } - - /** - * Sets the CSS3 transform-origin property - * - * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin - * @param string $val - */ - function set_transform_origin($val) - { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["transform_origin"] = null; - $this->_props["transform_origin"] = $val; - } - - /** - * Gets the CSS3 transform-origin property - * - * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin - * @return mixed[] - */ - function get_transform_origin() { - $values = preg_split("/\s+/", $this->_props['transform_origin']); - - if (count($values) === 0) { - $values = preg_split("/\s+/", self::$_defaults["transform_origin"]); - } - - $values = array_map(function($value) { - if (in_array($value, array("top", "left"))) { - return 0; - } else if (in_array($value, array("bottom", "right"))) { - return "100%"; - } else { - return $value; - } - }, $values); - - if (!isset($values[1])) { - $values[1] = $values[0]; - } - - return $values; - } - - /** - * @param $val - * @return null - */ - protected function parse_image_resolution($val) - { - // If exif data could be get: - // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/'; - - $re = '/^\s*(\d+|normal|auto)\s*$/'; - - if (!preg_match($re, $val, $matches)) { - return null; - } - - return $matches[1]; - } - - /** - * auto | normal | dpi - * - * @param $val - */ - function set_background_image_resolution($val) - { - $parsed = $this->parse_image_resolution($val); - - $this->_prop_cache["background_image_resolution"] = null; - $this->_props["background_image_resolution"] = $parsed; - } - - /** - * auto | normal | dpi - * - * @param $val - */ - function set_image_resolution($val) - { - $parsed = $this->parse_image_resolution($val); - - $this->_prop_cache["image_resolution"] = null; - $this->_props["image_resolution"] = $parsed; - } - - /** - * @param $val - */ - function set__dompdf_background_image_resolution($val) - { - $this->set_background_image_resolution($val); - } - - /** - * @param $val - */ - function set__dompdf_image_resolution($val) - { - $this->set_image_resolution($val); - } - - /** - * @param $val - */ - function set_z_index($val) - { - if (round($val) != $val && $val !== "auto") { - return; - } - - $this->_prop_cache["z_index"] = null; - $this->_props["z_index"] = $val; - } - - /** - * @param $val - */ - function set_counter_increment($val) - { - $val = trim($val); - $value = null; - - if (in_array($val, array("none", "inherit"))) { - $value = $val; - } else { - if (preg_match_all("/(" . self::CSS_IDENTIFIER . ")(?:\s+(" . self::CSS_INTEGER . "))?/", $val, $matches, PREG_SET_ORDER)) { - $value = array(); - foreach ($matches as $match) { - $value[$match[1]] = isset($match[2]) ? $match[2] : 1; - } - } - } - - $this->_prop_cache["counter_increment"] = null; - $this->_props["counter_increment"] = $value; - } - - /** - * @param FontMetrics $fontMetrics - * @return $this - */ - public function setFontMetrics(FontMetrics $fontMetrics) - { - $this->fontMetrics = $fontMetrics; - return $this; - } - - /** - * @return FontMetrics - */ - public function getFontMetrics() - { - return $this->fontMetrics; - } - - /** - * Generate a string representation of the Style - * - * This dumps the entire property array into a string via print_r. Useful - * for debugging. - * - * @return string - */ - /*DEBUGCSS print: see below additional debugging util*/ - function __toString() - { - return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size), - $this->_props), true); - } - - /*DEBUGCSS*/ - function debug_print() - { - /*DEBUGCSS*/ - print "parent_font_size:" . $this->_parent_font_size . ";\n"; - /*DEBUGCSS*/ - foreach ($this->_props as $prop => $val) { - /*DEBUGCSS*/ - print $prop . ':' . $val; - /*DEBUGCSS*/ - if (isset($this->_important_props[$prop])) { - /*DEBUGCSS*/ - print '!important'; - /*DEBUGCSS*/ - } - /*DEBUGCSS*/ - print ";\n"; - /*DEBUGCSS*/ - } - /*DEBUGCSS*/ - } -} diff --git a/library/vendor/dompdf/src/FontMetrics.php b/library/vendor/dompdf/src/FontMetrics.php deleted file mode 100644 index 712bd0acc..000000000 --- a/library/vendor/dompdf/src/FontMetrics.php +++ /dev/null @@ -1,538 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -namespace Dompdf; - -use FontLib\Font; - -/** - * The font metrics class - * - * This class provides information about fonts and text. It can resolve - * font names into actual installed font files, as well as determine the - * size of text in a particular font and size. - * - * @static - * @package dompdf - */ -class FontMetrics -{ - /** - * Name of the font cache file - * - * This file must be writable by the webserver process only to update it - * with save_font_families() after adding the .afm file references of a new font family - * with FontMetrics::saveFontFamilies(). - * This is typically done only from command line with load_font.php on converting - * ttf fonts to ufm with php-font-lib. - */ - const CACHE_FILE = "dompdf_font_family_cache.php"; - - /** - * @var Canvas - * @deprecated - */ - protected $pdf; - - /** - * Underlying {@link Canvas} object to perform text size calculations - * - * @var Canvas - */ - protected $canvas; - - /** - * Array of font family names to font files - * - * Usually cached by the {@link load_font.php} script - * - * @var array - */ - protected $fontLookup = array(); - - /** - * @var Options - */ - private $options; - - /** - * Class initialization - */ - public function __construct(Canvas $canvas, Options $options) - { - $this->setCanvas($canvas); - $this->setOptions($options); - $this->loadFontFamilies(); - } - - /** - * @deprecated - */ - public function save_font_families() - { - $this->saveFontFamilies(); - } - - /** - * Saves the stored font family cache - * - * The name and location of the cache file are determined by {@link - * FontMetrics::CACHE_FILE}. This file should be writable by the - * webserver process. - * - * @see FontMetrics::loadFontFamilies() - */ - public function saveFontFamilies() - { - // replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability) - $cacheData = sprintf("fontLookup as $family => $variants) { - $cacheData .= sprintf(" '%s' => array(%s", addslashes($family), PHP_EOL); - foreach ($variants as $variant => $path) { - $path = sprintf("'%s'", $path); - $path = str_replace('\'' . $this->getOptions()->getFontDir() , '$fontDir . \'' , $path); - $path = str_replace('\'' . $this->getOptions()->getRootDir() , '$rootDir . \'' , $path); - $cacheData .= sprintf(" '%s' => %s,%s", $variant, $path, PHP_EOL); - } - $cacheData .= sprintf(" ),%s", PHP_EOL); - } - $cacheData .= ") ?>"; - file_put_contents($this->getCacheFile(), $cacheData); - } - - /** - * @deprecated - */ - public function load_font_families() - { - $this->loadFontFamilies(); - } - - /** - * Loads the stored font family cache - * - * @see FontMetrics::saveFontFamilies() - */ - public function loadFontFamilies() - { - $fontDir = $this->getOptions()->getFontDir(); - $rootDir = $this->getOptions()->getRootDir(); - - // FIXME: temporarily define constants for cache files <= v0.6.2 - if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); } - if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); } - - $file = $rootDir . "/lib/fonts/dompdf_font_family_cache.dist.php"; - $distFonts = require $file; - - if (!is_readable($this->getCacheFile())) { - $this->fontLookup = $distFonts; - return; - } - - $cacheData = require $this->getCacheFile(); - - $this->fontLookup = array(); - if (is_array($this->fontLookup)) { - foreach ($cacheData as $key => $value) { - $this->fontLookup[stripslashes($key)] = $value; - } - } - - // Merge provided fonts - $this->fontLookup += $distFonts; - } - - /** - * @param array $style - * @param string $remote_file - * @param resource $context - * @return bool - * @deprecated - */ - public function register_font($style, $remote_file, $context = null) - { - return $this->registerFont($style, $remote_file); - } - - /** - * @param array $style - * @param string $remoteFile - * @param resource $context - * @return bool - */ - public function registerFont($style, $remoteFile, $context = null) - { - $fontname = mb_strtolower($style["family"]); - $families = $this->getFontFamilies(); - - $entry = array(); - if (isset($families[$fontname])) { - $entry = $families[$fontname]; - } - - $styleString = $this->getType("{$style['weight']} {$style['style']}"); - if (isset($entry[$styleString])) { - return true; - } - - $fontDir = $this->getOptions()->getFontDir(); - $remoteHash = md5($remoteFile); - $localFile = $fontDir . DIRECTORY_SEPARATOR . $remoteHash; - - $cacheEntry = $localFile; - $localFile .= ".".strtolower(pathinfo(parse_url($remoteFile, PHP_URL_PATH),PATHINFO_EXTENSION)); - - $entry[$styleString] = $cacheEntry; - - // Download the remote file - list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context); - if (false === $remoteFileContent) { - return false; - } - - $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-"); - file_put_contents($localTempFile, $remoteFileContent); - - $font = Font::load($localTempFile); - - if (!$font) { - unlink($localTempFile); - return false; - } - - $font->parse(); - $font->saveAdobeFontMetrics("$cacheEntry.ufm"); - $font->close(); - - unlink($localTempFile); - - if ( !file_exists("$cacheEntry.ufm") ) { - return false; - } - - // Save the changes - file_put_contents($localFile, $remoteFileContent); - - if ( !file_exists($localFile) ) { - unlink("$cacheEntry.ufm"); - return false; - } - - $this->setFontFamily($fontname, $entry); - $this->saveFontFamilies(); - - return true; - } - - /** - * @param $text - * @param $font - * @param $size - * @param float $word_spacing - * @param float $char_spacing - * @return float - * @deprecated - */ - public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) - { - //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); - return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); - } - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $wordSpacing - * @param float $charSpacing - * - * @internal param float $spacing word spacing, if any - * @return float - */ - public function getTextWidth($text, $font, $size, $wordSpacing = 0.0, $charSpacing = 0.0) - { - // @todo Make sure this cache is efficient before enabling it - static $cache = array(); - - if ($text === "") { - return 0; - } - - // Don't cache long strings - $useCache = !isset($text[50]); // Faster than strlen - - $key = "$font/$size/$wordSpacing/$charSpacing"; - - if ($useCache && isset($cache[$key][$text])) { - return $cache[$key]["$text"]; - } - - $width = $this->getCanvas()->get_text_width($text, $font, $size, $wordSpacing, $charSpacing); - - if ($useCache) { - $cache[$key][$text] = $width; - } - - return $width; - } - - /** - * @param $font - * @param $size - * @return float - * @deprecated - */ - public function get_font_height($font, $size) - { - return $this->getFontHeight($font, $size); - } - - /** - * Calculates font height - * - * @param string $font - * @param float $size - * - * @return float - */ - public function getFontHeight($font, $size) - { - return $this->getCanvas()->get_font_height($font, $size); - } - - /** - * @param $family_raw - * @param string $subtype_raw - * @return string - * @deprecated - */ - public function get_font($family_raw, $subtype_raw = "normal") - { - return $this->getFont($family_raw, $subtype_raw); - } - - /** - * Resolves a font family & subtype into an actual font file - * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If - * the particular font family has no suitable font file, the default font - * ({@link Options::defaultFont}) is used. The font file returned - * is the absolute pathname to the font file on the system. - * - * @param string $familyRaw - * @param string $subtypeRaw - * - * @return string - */ - public function getFont($familyRaw, $subtypeRaw = "normal") - { - static $cache = array(); - - if (isset($cache[$familyRaw][$subtypeRaw])) { - return $cache[$familyRaw][$subtypeRaw]; - } - - /* Allow calling for various fonts in search path. Therefore not immediately - * return replacement on non match. - * Only when called with NULL try replacement. - * When this is also missing there is really trouble. - * If only the subtype fails, nevertheless return failure. - * Only on checking the fallback font, check various subtypes on same font. - */ - - $subtype = strtolower($subtypeRaw); - - if ($familyRaw) { - $family = str_replace(array("'", '"'), "", strtolower($familyRaw)); - - if (isset($this->fontLookup[$family][$subtype])) { - return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype]; - } - - return null; - } - - $family = "serif"; - - if (isset($this->fontLookup[$family][$subtype])) { - return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype]; - } - - if (!isset($this->fontLookup[$family])) { - return null; - } - - $family = $this->fontLookup[$family]; - - foreach ($family as $sub => $font) { - if (strpos($subtype, $sub) !== false) { - return $cache[$familyRaw][$subtypeRaw] = $font; - } - } - - if ($subtype !== "normal") { - foreach ($family as $sub => $font) { - if ($sub !== "normal") { - return $cache[$familyRaw][$subtypeRaw] = $font; - } - } - } - - $subtype = "normal"; - - if (isset($family[$subtype])) { - return $cache[$familyRaw][$subtypeRaw] = $family[$subtype]; - } - - return null; - } - - /** - * @param $family - * @return null|string - * @deprecated - */ - public function get_family($family) - { - return $this->getFamily($family); - } - - /** - * @param string $family - * @return null|string - */ - public function getFamily($family) - { - $family = str_replace(array("'", '"'), "", mb_strtolower($family)); - - if (isset($this->fontLookup[$family])) { - return $this->fontLookup[$family]; - } - - return null; - } - - /** - * @param $type - * @return string - * @deprecated - */ - public function get_type($type) - { - return $this->getType($type); - } - - /** - * @param string $type - * @return string - */ - public function getType($type) - { - if (preg_match("/bold/i", $type)) { - if (preg_match("/italic|oblique/i", $type)) { - $type = "bold_italic"; - } else { - $type = "bold"; - } - } elseif (preg_match("/italic|oblique/i", $type)) { - $type = "italic"; - } else { - $type = "normal"; - } - - return $type; - } - - /** - * @return array - * @deprecated - */ - public function get_font_families() - { - return $this->getFontFamilies(); - } - - /** - * Returns the current font lookup table - * - * @return array - */ - public function getFontFamilies() - { - return $this->fontLookup; - } - - /** - * @param string $fontname - * @param mixed $entry - * @deprecated - */ - public function set_font_family($fontname, $entry) - { - $this->setFontFamily($fontname, $entry); - } - - /** - * @param string $fontname - * @param mixed $entry - */ - public function setFontFamily($fontname, $entry) - { - $this->fontLookup[mb_strtolower($fontname)] = $entry; - } - - /** - * @return string - */ - public function getCacheFile() - { - return $this->getOptions()->getFontDir() . DIRECTORY_SEPARATOR . self::CACHE_FILE; - } - - /** - * @param Options $options - * @return $this - */ - public function setOptions(Options $options) - { - $this->options = $options; - return $this; - } - - /** - * @return Options - */ - public function getOptions() - { - return $this->options; - } - - /** - * @param Canvas $canvas - * @return $this - */ - public function setCanvas(Canvas $canvas) - { - $this->canvas = $canvas; - // Still write deprecated pdf for now. It might be used by a parent class. - $this->pdf = $canvas; - return $this; - } - - /** - * @return Canvas - */ - public function getCanvas() - { - return $this->canvas; - } -} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/FrameList.php b/library/vendor/dompdf/src/Frame/FrameList.php deleted file mode 100644 index 37d9990a1..000000000 --- a/library/vendor/dompdf/src/Frame/FrameList.php +++ /dev/null @@ -1,35 +0,0 @@ -_frame = $frame; - } - - /** - * @return FrameListIterator - */ - function getIterator() - { - return new FrameListIterator($this->_frame); - } -} diff --git a/library/vendor/dompdf/src/Frame/FrameListIterator.php b/library/vendor/dompdf/src/Frame/FrameListIterator.php deleted file mode 100644 index ada9dde18..000000000 --- a/library/vendor/dompdf/src/Frame/FrameListIterator.php +++ /dev/null @@ -1,91 +0,0 @@ -_parent = $frame; - $this->_cur = $frame->get_first_child(); - $this->_num = 0; - } - - /** - * - */ - public function rewind() - { - $this->_cur = $this->_parent->get_first_child(); - $this->_num = 0; - } - - /** - * @return bool - */ - public function valid() - { - return isset($this->_cur); // && ($this->_cur->get_prev_sibling() === $this->_prev); - } - - /** - * @return int - */ - public function key() - { - return $this->_num; - } - - /** - * @return Frame - */ - public function current() - { - return $this->_cur; - } - - /** - * @return Frame - */ - public function next() - { - $ret = $this->_cur; - if (!$ret) { - return null; - } - - $this->_cur = $this->_cur->get_next_sibling(); - $this->_num++; - return $ret; - } -} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/FrameTreeList.php b/library/vendor/dompdf/src/Frame/FrameTreeList.php deleted file mode 100644 index f8b996c68..000000000 --- a/library/vendor/dompdf/src/Frame/FrameTreeList.php +++ /dev/null @@ -1,35 +0,0 @@ -_root = $root; - } - - /** - * @return FrameTreeIterator - */ - public function getIterator() - { - return new FrameTreeIterator($this->_root); - } -} diff --git a/library/vendor/dompdf/src/FrameDecorator/Block.php b/library/vendor/dompdf/src/FrameDecorator/Block.php deleted file mode 100644 index b65b3e104..000000000 --- a/library/vendor/dompdf/src/FrameDecorator/Block.php +++ /dev/null @@ -1,284 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameDecorator; - -use Dompdf\Dompdf; -use Dompdf\Frame; -use Dompdf\LineBox; - -/** - * Decorates frames for block layout - * - * @access private - * @package dompdf - */ -class Block extends AbstractFrameDecorator -{ - /** - * Current line index - * - * @var int - */ - protected $_cl; - - /** - * The block's line boxes - * - * @var LineBox[] - */ - protected $_line_boxes; - - /** - * Block constructor. - * @param Frame $frame - * @param Dompdf $dompdf - */ - function __construct(Frame $frame, Dompdf $dompdf) - { - parent::__construct($frame, $dompdf); - - $this->_line_boxes = array(new LineBox($this)); - $this->_cl = 0; - } - - /** - * - */ - function reset() - { - parent::reset(); - - $this->_line_boxes = array(new LineBox($this)); - $this->_cl = 0; - } - - /** - * @return LineBox - */ - function get_current_line_box() - { - return $this->_line_boxes[$this->_cl]; - } - - /** - * @return integer - */ - function get_current_line_number() - { - return $this->_cl; - } - - /** - * @return LineBox[] - */ - function get_line_boxes() - { - return $this->_line_boxes; - } - - /** - * @param integer $line_number - * @return integer - */ - function set_current_line_number($line_number) - { - $line_boxes_count = count($this->_line_boxes); - $cl = max(min($line_number, $line_boxes_count), 0); - return ($this->_cl = $cl); - } - - /** - * @param integer $i - */ - function clear_line($i) - { - if (isset($this->_line_boxes[$i])) { - unset($this->_line_boxes[$i]); - } - } - - /** - * @param Frame $frame - */ - function add_frame_to_line(Frame $frame) - { - if (!$frame->is_in_flow()) { - return; - } - - $style = $frame->get_style(); - - $frame->set_containing_line($this->_line_boxes[$this->_cl]); - - /* - // Adds a new line after a block, only if certain conditions are met - if ((($frame instanceof Inline && $frame->get_node()->nodeName !== "br") || - $frame instanceof Text && trim($frame->get_text())) && - ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" && - $this->_line_boxes[$this->_cl]->w > 0 )) { - - $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame ); - $this->add_line(); - - // Add each child of the inline frame to the line individually - foreach ($frame->get_children() as $child) - $this->add_frame_to_line( $child ); - } - else*/ - - // Handle inline frames (which are effectively wrappers) - if ($frame instanceof Inline) { - // Handle line breaks - if ($frame->get_node()->nodeName === "br") { - $this->maximize_line_height($style->length_in_pt($style->line_height), $frame); - $this->add_line(true); - } - - return; - } - - // Trim leading text if this is an empty line. Kinda a hack to put it here, - // but what can you do... - if ($this->get_current_line_box()->w == 0 && - $frame->is_text_node() && - !$frame->is_pre() - ) { - $frame->set_text(ltrim($frame->get_text())); - $frame->recalculate_width(); - } - - $w = $frame->get_margin_width(); - - // FIXME: Why? Doesn't quite seem to be the correct thing to do, - // but does appear to be necessary. Hack to handle wrapped white space? - if ($w == 0 && $frame->get_node()->nodeName !== "hr") { - return; - } - - // Debugging code: - /* - Helpers::pre_r("\n

Adding frame to line:

"); - - // Helpers::pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")"); - // Helpers::pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")"); - if ( $frame->is_text_node() ) - Helpers::pre_r('"'.$frame->get_node()->nodeValue.'"'); - - Helpers::pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w); - Helpers::pre_r("Frame: " . get_class($frame)); - Helpers::pre_r("Frame width: " . $w); - Helpers::pre_r("Frame height: " . $frame->get_margin_height()); - Helpers::pre_r("Containing block width: " . $this->get_containing_block("w")); - */ - // End debugging - - $line = $this->_line_boxes[$this->_cl]; - if ($line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) { - $this->add_line(); - } - - $frame->position(); - - $current_line = $this->_line_boxes[$this->_cl]; - $current_line->add_frame($frame); - - if ($frame->is_text_node()) { - $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text()))); - } - - $this->increase_line_width($w); - - $this->maximize_line_height($frame->get_margin_height(), $frame); - } - - /** - * @param Frame $frame - */ - function remove_frames_from_line(Frame $frame) - { - // Search backwards through the lines for $frame - $i = $this->_cl; - $j = null; - - while ($i >= 0) { - if (($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false) { - break; - } - - $i--; - } - - if ($j === false) { - return; - } - - // Remove $frame and all frames that follow - while ($j < count($this->_line_boxes[$i]->get_frames())) { - $frames = $this->_line_boxes[$i]->get_frames(); - $f = $frames[$j]; - $frames[$j] = null; - unset($frames[$j]); - $j++; - $this->_line_boxes[$i]->w -= $f->get_margin_width(); - } - - // Recalculate the height of the line - $h = 0; - foreach ($this->_line_boxes[$i]->get_frames() as $f) { - $h = max($h, $f->get_margin_height()); - } - - $this->_line_boxes[$i]->h = $h; - - // Remove all lines that follow - while ($this->_cl > $i) { - $this->_line_boxes[$this->_cl] = null; - unset($this->_line_boxes[$this->_cl]); - $this->_cl--; - } - } - - /** - * @param float $w - */ - function increase_line_width($w) - { - $this->_line_boxes[$this->_cl]->w += $w; - } - - /** - * @param $val - * @param Frame $frame - */ - function maximize_line_height($val, Frame $frame) - { - if ($val > $this->_line_boxes[$this->_cl]->h) { - $this->_line_boxes[$this->_cl]->tallest_frame = $frame; - $this->_line_boxes[$this->_cl]->h = $val; - } - } - - /** - * @param bool $br - */ - function add_line($br = false) - { - -// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 || -// return; - - $this->_line_boxes[$this->_cl]->br = $br; - $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h; - - $new_line = new LineBox($this, $y); - - $this->_line_boxes[++$this->_cl] = $new_line; - } - - //........................................................................ -} diff --git a/library/vendor/dompdf/src/FrameDecorator/Inline.php b/library/vendor/dompdf/src/FrameDecorator/Inline.php deleted file mode 100644 index e831fd85e..000000000 --- a/library/vendor/dompdf/src/FrameDecorator/Inline.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameDecorator; - -use DOMElement; -use Dompdf\Dompdf; -use Dompdf\Frame; -use Dompdf\Exception; - -/** - * Decorates frames for inline layout - * - * @access private - * @package dompdf - */ -class Inline extends AbstractFrameDecorator -{ - - /** - * Inline constructor. - * @param Frame $frame - * @param Dompdf $dompdf - */ - function __construct(Frame $frame, Dompdf $dompdf) - { - parent::__construct($frame, $dompdf); - } - - /** - * @param Frame|null $frame - * @param bool $force_pagebreak - * @throws Exception - */ - function split(Frame $frame = null, $force_pagebreak = false) - { - if (is_null($frame)) { - $this->get_parent()->split($this, $force_pagebreak); - return; - } - - if ($frame->get_parent() !== $this) { - throw new Exception("Unable to split: frame is not a child of this one."); - } - - $node = $this->_frame->get_node(); - - if ($node instanceof DOMElement && $node->hasAttribute("id")) { - $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); - $node->removeAttribute("id"); - } - - $split = $this->copy($node->cloneNode()); - // if this is a generated node don't propagate the content style - if ($split->get_node()->nodeName == "dompdf_generated") { - $split->get_style()->content = "normal"; - } - $this->get_parent()->insert_child_after($split, $this); - - // Unset the current node's right style properties - $style = $this->_frame->get_style(); - $style->margin_right = 0; - $style->padding_right = 0; - $style->border_right_width = 0; - - // Unset the split node's left style properties since we don't want them - // to propagate - $style = $split->get_style(); - $style->margin_left = 0; - $style->padding_left = 0; - $style->border_left_width = 0; - - //On continuation of inline element on next line, - //don't repeat non-vertically repeatble background images - //See e.g. in testcase image_variants, long desriptions - if (($url = $style->background_image) && $url !== "none" - && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y" - ) { - $style->background_image = "none"; - } - - // Add $frame and all following siblings to the new split node - $iter = $frame; - while ($iter) { - $frame = $iter; - $iter = $iter->get_next_sibling(); - $frame->reset(); - $split->append_child($frame); - } - - $page_breaks = array("always", "left", "right"); - $frame_style = $frame->get_style(); - if ($force_pagebreak || - in_array($frame_style->page_break_before, $page_breaks) || - in_array($frame_style->page_break_after, $page_breaks) - ) { - $this->get_parent()->split($split, true); - } - } - -} diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBullet.php b/library/vendor/dompdf/src/FrameDecorator/ListBullet.php deleted file mode 100644 index 5b2311886..000000000 --- a/library/vendor/dompdf/src/FrameDecorator/ListBullet.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameDecorator; - -use Dompdf\Dompdf; -use Dompdf\Frame; - -/** - * Decorates frames for list bullet rendering - * - * @package dompdf - */ -class ListBullet extends AbstractFrameDecorator -{ - - const BULLET_PADDING = 1; // Distance from bullet to text in pt - // As fraction of font size (including descent). See also DECO_THICKNESS. - const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04 - const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now. - const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent. - - static $BULLET_TYPES = array("disc", "circle", "square"); - - /** - * ListBullet constructor. - * @param Frame $frame - * @param Dompdf $dompdf - */ - function __construct(Frame $frame, Dompdf $dompdf) - { - parent::__construct($frame, $dompdf); - } - - /** - * @return float|int - */ - function get_margin_width() - { - $style = $this->_frame->get_style(); - - if ($style->list_style_type === "none") { - return 0; - } - - return $style->font_size * self::BULLET_SIZE + 2 * self::BULLET_PADDING; - } - - /** - * hits only on "inset" lists items, to increase height of box - * - * @return float|int - */ - function get_margin_height() - { - $style = $this->_frame->get_style(); - - if ($style->list_style_type === "none") { - return 0; - } - - return $style->font_size * self::BULLET_SIZE + 2 * self::BULLET_PADDING; - } - - /** - * @return float|int - */ - function get_width() - { - return $this->get_margin_width(); - } - - /** - * @return float|int - */ - function get_height() - { - return $this->get_margin_height(); - } - - //........................................................................ -} diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php b/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php deleted file mode 100644 index 5bd93a5de..000000000 --- a/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php +++ /dev/null @@ -1,154 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameDecorator; - -use Dompdf\Dompdf; -use Dompdf\Frame; -use Dompdf\Helpers; - -/** - * Decorates frames for list bullets with custom images - * - * @package dompdf - */ -class ListBulletImage extends AbstractFrameDecorator -{ - - /** - * The underlying image frame - * - * @var Image - */ - protected $_img; - - /** - * The image's width in pixels - * - * @var int - */ - protected $_width; - - /** - * The image's height in pixels - * - * @var int - */ - protected $_height; - - /** - * Class constructor - * - * @param Frame $frame the bullet frame to decorate - * @param Dompdf $dompdf the document's dompdf object - */ - function __construct(Frame $frame, Dompdf $dompdf) - { - $style = $frame->get_style(); - $url = $style->list_style_image; - $frame->get_node()->setAttribute("src", $url); - $this->_img = new Image($frame, $dompdf); - parent::__construct($this->_img, $dompdf); - list($width, $height) = Helpers::dompdf_getimagesize($this->_img->get_image_url(), $dompdf->getHttpContext()); - - // Resample the bullet image to be consistent with 'auto' sized images - // See also Image::get_min_max_width - // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. - $dpi = $this->_dompdf->getOptions()->getDpi(); - $this->_width = ((float)rtrim($width, "px") * 72) / $dpi; - $this->_height = ((float)rtrim($height, "px") * 72) / $dpi; - - //If an image is taller as the containing block/box, the box should be extended. - //Neighbour elements are overwriting the overlapping image areas. - //Todo: Where can the box size be extended? - //Code below has no effect. - //See block_frame_reflower _calculate_restricted_height - //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S. - //Leave for now - //if ($style->min_height < $this->_height ) { - // $style->min_height = $this->_height; - //} - //$style->height = "auto"; - } - - /** - * Return the bullet's width - * - * @return int - */ - function get_width() - { - //ignore image width, use same width as on predefined bullet ListBullet - //for proper alignment of bullet image and text. Allow image to not fitting on left border. - //This controls the distance between bullet image and text - //return $this->_width; - return $this->_frame->get_style()->get_font_size() * ListBullet::BULLET_SIZE + - 2 * ListBullet::BULLET_PADDING; - } - - /** - * Return the bullet's height - * - * @return int - */ - function get_height() - { - //based on image height - return $this->_height; - } - - /** - * Override get_margin_width - * - * @return int - */ - function get_margin_width() - { - //ignore image width, use same width as on predefined bullet ListBullet - //for proper alignment of bullet image and text. Allow image to not fitting on left border. - //This controls the extra indentation of text to make room for the bullet image. - //Here use actual image size, not predefined bullet size - //return $this->_frame->get_style()->get_font_size()*ListBullet::BULLET_SIZE + - // 2 * ListBullet::BULLET_PADDING; - - // Small hack to prevent indenting of list text - // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none. - if ($this->_frame->get_style()->list_style_position === "outside" || $this->_width == 0) { - return 0; - } - //This aligns the "inside" image position with the text. - //The text starts to the right of the image. - //Between the image and the text there is an added margin of image width. - //Where this comes from is unknown. - //The corresponding ListBullet sets a smaller margin. bullet size? - return $this->_width + 2 * ListBullet::BULLET_PADDING; - } - - /** - * Override get_margin_height() - * - * @return int - */ - function get_margin_height() - { - //Hits only on "inset" lists items, to increase height of box - //based on image height - return $this->_height + 2 * ListBullet::BULLET_PADDING; - } - - /** - * Return image url - * - * @return string - */ - function get_image_url() - { - return $this->_img->get_image_url(); - } - -} diff --git a/library/vendor/dompdf/src/FrameDecorator/Table.php b/library/vendor/dompdf/src/FrameDecorator/Table.php deleted file mode 100644 index 3776c60be..000000000 --- a/library/vendor/dompdf/src/FrameDecorator/Table.php +++ /dev/null @@ -1,398 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameDecorator; - -use Dompdf\Cellmap; -use DOMNode; -use Dompdf\Dompdf; -use Dompdf\Frame; -use Dompdf\Frame\Factory; - -/** - * Decorates Frames for table layout - * - * @package dompdf - */ -class Table extends AbstractFrameDecorator -{ - public static $VALID_CHILDREN = array( - "table-row-group", - "table-row", - "table-header-group", - "table-footer-group", - "table-column", - "table-column-group", - "table-caption", - "table-cell" - ); - - public static $ROW_GROUPS = array( - 'table-row-group', - 'table-header-group', - 'table-footer-group' - ); - - /** - * The Cellmap object for this table. The cellmap maps table cells - * to rows and columns, and aids in calculating column widths. - * - * @var \Dompdf\Cellmap - */ - protected $_cellmap; - - /** - * The minimum width of the table, in pt - * - * @var float - */ - protected $_min_width; - - /** - * The maximum width of the table, in pt - * - * @var float - */ - protected $_max_width; - - /** - * Table header rows. Each table header is duplicated when a table - * spans pages. - * - * @var array - */ - protected $_headers; - - /** - * Table footer rows. Each table footer is duplicated when a table - * spans pages. - * - * @var array - */ - protected $_footers; - - /** - * Class constructor - * - * @param Frame $frame the frame to decorate - * @param Dompdf $dompdf - */ - public function __construct(Frame $frame, Dompdf $dompdf) - { - parent::__construct($frame, $dompdf); - $this->_cellmap = new Cellmap($this); - - if ($frame->get_style()->table_layout === "fixed") { - $this->_cellmap->set_layout_fixed(true); - } - - $this->_min_width = null; - $this->_max_width = null; - $this->_headers = array(); - $this->_footers = array(); - } - - public function reset() - { - parent::reset(); - $this->_cellmap->reset(); - $this->_min_width = null; - $this->_max_width = null; - $this->_headers = array(); - $this->_footers = array(); - $this->_reflower->reset(); - } - - //........................................................................ - - /** - * split the table at $row. $row and all subsequent rows will be - * added to the clone. This method is overidden in order to remove - * frames from the cellmap properly. - * - * @param Frame $child - * @param bool $force_pagebreak - * - * @return void - */ - public function split(Frame $child = null, $force_pagebreak = false) - { - if (is_null($child)) { - parent::split(); - - return; - } - - // If $child is a header or if it is the first non-header row, do - // not duplicate headers, simply move the table to the next page. - if (count($this->_headers) && !in_array($child, $this->_headers, true) && - !in_array($child->get_prev_sibling(), $this->_headers, true) - ) { - $first_header = null; - - // Insert copies of the table headers before $child - foreach ($this->_headers as $header) { - - $new_header = $header->deep_copy(); - - if (is_null($first_header)) { - $first_header = $new_header; - } - - $this->insert_child_before($new_header, $child); - } - - parent::split($first_header); - - } elseif (in_array($child->get_style()->display, self::$ROW_GROUPS)) { - - // Individual rows should have already been handled - parent::split($child); - - } else { - - $iter = $child; - - while ($iter) { - $this->_cellmap->remove_row($iter); - $iter = $iter->get_next_sibling(); - } - - parent::split($child); - } - } - - /** - * Return a copy of this frame with $node as its node - * - * @param DOMNode $node - * - * @return Frame - */ - public function copy(DOMNode $node) - { - $deco = parent::copy($node); - - // In order to keep columns' widths through pages - $deco->_cellmap->set_columns($this->_cellmap->get_columns()); - $deco->_cellmap->lock_columns(); - - return $deco; - } - - /** - * Static function to locate the parent table of a frame - * - * @param Frame $frame - * - * @return Table the table that is an ancestor of $frame - */ - public static function find_parent_table(Frame $frame) - { - while ($frame = $frame->get_parent()) { - if ($frame->is_table()) { - break; - } - } - - return $frame; - } - - /** - * Return this table's Cellmap - * - * @return \Dompdf\Cellmap - */ - public function get_cellmap() - { - return $this->_cellmap; - } - - /** - * Return the minimum width of this table - * - * @return float - */ - public function get_min_width() - { - return $this->_min_width; - } - - /** - * Return the maximum width of this table - * - * @return float - */ - public function get_max_width() - { - return $this->_max_width; - } - - /** - * Set the minimum width of the table - * - * @param float $width the new minimum width - */ - public function set_min_width($width) - { - $this->_min_width = $width; - } - - /** - * Set the maximum width of the table - * - * @param float $width the new maximum width - */ - public function set_max_width($width) - { - $this->_max_width = $width; - } - - /** - * Restructure tree so that the table has the correct structure. - * Invalid children (i.e. all non-table-rows) are moved below the - * table. - * - * @fixme #1363 Method has some bugs. $table_row has not been initialized and lookup most likely could return an - * array of Style instead a Style Object - */ - public function normalise() - { - // Store frames generated by invalid tags and move them outside the table - $erroneous_frames = array(); - $anon_row = false; - $iter = $this->get_first_child(); - while ($iter) { - $child = $iter; - $iter = $iter->get_next_sibling(); - - $display = $child->get_style()->display; - - if ($anon_row) { - - if ($display === "table-row") { - // Add the previous anonymous row - $this->insert_child_before($table_row, $child); - - $table_row->normalise(); - $child->normalise(); - $this->_cellmap->add_row(); - $anon_row = false; - continue; - } - - // add the child to the anonymous row - $table_row->append_child($child); - continue; - - } else { - - if ($display === "table-row") { - $child->normalise(); - continue; - } - - if ($display === "table-cell") { - $css = $this->get_style()->get_stylesheet(); - - // Create an anonymous table row group - $tbody = $this->get_node()->ownerDocument->createElement("tbody"); - - $frame = new Frame($tbody); - - $style = $css->create_style(); - $style->inherit($this->get_style()); - - // Lookup styles for tbody tags. If the user wants styles to work - // better, they should make the tbody explicit... I'm not going to - // try to guess what they intended. - if ($tbody_style = $css->lookup("tbody")) { - $style->merge($tbody_style); - } - $style->display = 'table-row-group'; - - // Okay, I have absolutely no idea why I need this clone here, but - // if it's omitted, php (as of 2004-07-28) segfaults. - $frame->set_style($style); - $table_row_group = Factory::decorate_frame($frame, $this->_dompdf, $this->_root); - - // Create an anonymous table row - $tr = $this->get_node()->ownerDocument->createElement("tr"); - - $frame = new Frame($tr); - - $style = $css->create_style(); - $style->inherit($this->get_style()); - - // Lookup styles for tr tags. If the user wants styles to work - // better, they should make the tr explicit... I'm not going to - // try to guess what they intended. - if ($tr_style = $css->lookup("tr")) { - $style->merge($tr_style); - } - $style->display = 'table-row'; - - // Okay, I have absolutely no idea why I need this clone here, but - // if it's omitted, php (as of 2004-07-28) segfaults. - $frame->set_style(clone $style); - $table_row = Factory::decorate_frame($frame, $this->_dompdf, $this->_root); - - // Add the cell to the row - $table_row->append_child($child, true); - - // Add the tr to the tbody - $table_row_group->append_child($table_row, true); - - $anon_row = true; - continue; - } - - if (!in_array($display, self::$VALID_CHILDREN)) { - $erroneous_frames[] = $child; - continue; - } - - // Normalise other table parts (i.e. row groups) - foreach ($child->get_children() as $grandchild) { - if ($grandchild->get_style()->display === "table-row") { - $grandchild->normalise(); - } - } - - // Add headers and footers - if ($display === "table-header-group") { - $this->_headers[] = $child; - } elseif ($display === "table-footer-group") { - $this->_footers[] = $child; - } - } - } - - if ($anon_row && $table_row_group instanceof AbstractFrameDecorator) { - // Add the row to the table - $this->_frame->append_child($table_row_group->_frame); - $table_row->normalise(); - } - - foreach ($erroneous_frames as $frame) { - $this->move_after($frame); - } - } - - //........................................................................ - - /** - * Moves the specified frame and it's corresponding node outside of - * the table. - * - * @param Frame $frame the frame to move - */ - public function move_after(Frame $frame) - { - $this->get_parent()->insert_child_after($frame, $this); - } -} \ No newline at end of file diff --git a/library/vendor/dompdf/src/FrameDecorator/TableRow.php b/library/vendor/dompdf/src/FrameDecorator/TableRow.php deleted file mode 100644 index 4c6dcb5f9..000000000 --- a/library/vendor/dompdf/src/FrameDecorator/TableRow.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameDecorator; - -use Dompdf\Dompdf; -use Dompdf\Frame; -use Dompdf\FrameDecorator\Table as TableFrameDecorator; - -/** - * Decorates Frames for table row layout - * - * @package dompdf - */ -class TableRow extends AbstractFrameDecorator -{ - /** - * TableRow constructor. - * @param Frame $frame - * @param Dompdf $dompdf - */ - function __construct(Frame $frame, Dompdf $dompdf) - { - parent::__construct($frame, $dompdf); - } - - //........................................................................ - - /** - * Remove all non table-cell frames from this row and move them after - * the table. - */ - function normalise() - { - // Find our table parent - $p = TableFrameDecorator::find_parent_table($this); - - $erroneous_frames = array(); - foreach ($this->get_children() as $child) { - $display = $child->get_style()->display; - - if ($display !== "table-cell") { - $erroneous_frames[] = $child; - } - } - - // dump the extra nodes after the table. - foreach ($erroneous_frames as $frame) { - $p->move_after($frame); - } - } - - function split(Frame $child = null, $force_pagebreak = false) - { - $this->_already_pushed = true; - - if (is_null($child)) { - parent::split(); - return; - } - - parent::split($child, $force_pagebreak); - } -} diff --git a/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php b/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php deleted file mode 100644 index 946d0969e..000000000 --- a/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php +++ /dev/null @@ -1,529 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameReflower; - -use Dompdf\Adapter\CPDF; -use Dompdf\Css\Style; -use Dompdf\Dompdf; -use Dompdf\Helpers; -use Dompdf\Frame; -use Dompdf\FrameDecorator\Block; -use Dompdf\Frame\Factory; - -/** - * Base reflower class - * - * Reflower objects are responsible for determining the width and height of - * individual frames. They also create line and page breaks as necessary. - * - * @package dompdf - */ -abstract class AbstractFrameReflower -{ - - /** - * Frame for this reflower - * - * @var Frame - */ - protected $_frame; - - /** - * Cached min/max size - * - * @var array - */ - protected $_min_max_cache; - - /** - * AbstractFrameReflower constructor. - * @param Frame $frame - */ - function __construct(Frame $frame) - { - $this->_frame = $frame; - $this->_min_max_cache = null; - } - - function dispose() - { - } - - /** - * @return Dompdf - */ - function get_dompdf() - { - return $this->_frame->get_dompdf(); - } - - /** - * Collapse frames margins - * http://www.w3.org/TR/CSS2/box.html#collapsing-margins - */ - protected function _collapse_margins() - { - $frame = $this->_frame; - $cb = $frame->get_containing_block(); - $style = $frame->get_style(); - - // Margins of float/absolutely positioned/inline-block elements do not collapse. - if (!$frame->is_in_flow() || $frame->is_inline_block()) { - return; - } - - $t = $style->length_in_pt($style->margin_top, $cb["h"]); - $b = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - // Handle 'auto' values - if ($t === "auto") { - $style->margin_top = "0pt"; - $t = 0; - } - - if ($b === "auto") { - $style->margin_bottom = "0pt"; - $b = 0; - } - - // Collapse vertical margins: - $n = $frame->get_next_sibling(); - if ( $n && !$n->is_block() & !$n->is_table() ) { - while ($n = $n->get_next_sibling()) { - if ($n->is_block() || $n->is_table()) { - break; - } - - if (!$n->get_first_child()) { - $n = null; - break; - } - } - } - - if ($n) { - $n_style = $n->get_style(); - $n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["h"]); - - $b = $this->_get_collapsed_margin_length($b, $n_t); - $style->margin_bottom = $b . "pt"; - $n_style->margin_top = "0pt"; - } - - // Collapse our first child's margin, if there is no border or padding - if ($style->border_top_width == 0 && $style->length_in_pt($style->padding_top) == 0) { - $f = $this->_frame->get_first_child(); - if ( $f && !$f->is_block() && !$f->is_table() ) { - while ( $f = $f->get_next_sibling() ) { - if ( $f->is_block() || $f->is_table() ) { - break; - } - - if ( !$f->get_first_child() ) { - $f = null; - break; - } - } - } - - // Margin are collapsed only between block-level boxes - if ($f) { - $f_style = $f->get_style(); - $f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["h"]); - - $t = $this->_get_collapsed_margin_length($t, $f_t); - $style->margin_top = $t."pt"; - $f_style->margin_top = "0pt"; - } - } - - // Collapse our last child's margin, if there is no border or padding - if ($style->border_bottom_width == 0 && $style->length_in_pt($style->padding_bottom) == 0) { - $l = $this->_frame->get_last_child(); - if ( $l && !$l->is_block() && !$l->is_table() ) { - while ( $l = $l->get_prev_sibling() ) { - if ( $l->is_block() || $l->is_table() ) { - break; - } - - if ( !$l->get_last_child() ) { - $l = null; - break; - } - } - } - - // Margin are collapsed only between block-level boxes - if ($l) { - $l_style = $l->get_style(); - $l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["h"]); - - $b = $this->_get_collapsed_margin_length($b, $l_b); - $style->margin_bottom = $b."pt"; - $l_style->margin_bottom = "0pt"; - } - } - } - - /** - * Get the combined (collapsed) length of two adjoining margins. - * - * See http://www.w3.org/TR/CSS2/box.html#collapsing-margins. - * - * @param number $length1 - * @param number $length2 - * @return number - */ - private function _get_collapsed_margin_length($length1, $length2) - { - if ($length1 < 0 && $length2 < 0) { - return min($length1, $length2); // min(x, y) = - max(abs(x), abs(y)), if x < 0 && y < 0 - } - - if ($length1 < 0 || $length2 < 0) { - return $length1 + $length2; // x + y = x - abs(y), if y < 0 - } - - return max($length1, $length2); - } - - /** - * @param Block|null $block - * @return mixed - */ - abstract function reflow(Block $block = null); - - /** - * Required for table layout: Returns an array(0 => min, 1 => max, "min" - * => min, "max" => max) of the minimum and maximum widths of this frame. - * This provides a basic implementation. Child classes should override - * this if necessary. - * - * @return array|null - */ - function get_min_max_width() - { - if (!is_null($this->_min_max_cache)) { - return $this->_min_max_cache; - } - - $style = $this->_frame->get_style(); - - // Account for margins & padding - $dims = array($style->padding_left, - $style->padding_right, - $style->border_left_width, - $style->border_right_width, - $style->margin_left, - $style->margin_right); - - $cb_w = $this->_frame->get_containing_block("w"); - $delta = (float)$style->length_in_pt($dims, $cb_w); - - // Handle degenerate case - if (!$this->_frame->get_first_child()) { - return $this->_min_max_cache = array( - $delta, $delta, - "min" => $delta, - "max" => $delta, - ); - } - - $low = array(); - $high = array(); - - for ($iter = $this->_frame->get_children()->getIterator(); $iter->valid(); $iter->next()) { - $inline_min = 0; - $inline_max = 0; - - // Add all adjacent inline widths together to calculate max width - while ($iter->valid() && in_array($iter->current()->get_style()->display, Style::$INLINE_TYPES)) { - $child = $iter->current(); - - $minmax = $child->get_min_max_width(); - - if (in_array($iter->current()->get_style()->white_space, array("pre", "nowrap"))) { - $inline_min += $minmax["min"]; - } else { - $low[] = $minmax["min"]; - } - - $inline_max += $minmax["max"]; - $iter->next(); - } - - if ($inline_max > 0) { - $high[] = $inline_max; - } - if ($inline_min > 0) { - $low[] = $inline_min; - } - - if ($iter->valid()) { - list($low[], $high[]) = $iter->current()->get_min_max_width(); - continue; - } - } - $min = count($low) ? max($low) : 0; - $max = count($high) ? max($high) : 0; - - // Use specified width if it is greater than the minimum defined by the - // content. If the width is a percentage ignore it for now. - $width = $style->width; - if ($width !== "auto" && !Helpers::is_percent($width)) { - $width = (float)$style->length_in_pt($width, $cb_w); - if ($min < $width) { - $min = $width; - } - if ($max < $width) { - $max = $width; - } - } - - $min += $delta; - $max += $delta; - return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max); - } - - /** - * Parses a CSS string containing quotes and escaped hex characters - * - * @param $string string The CSS string to parse - * @param $single_trim - * @return string - */ - protected function _parse_string($string, $single_trim = false) - { - if ($single_trim) { - $string = preg_replace('/^[\"\']/', "", $string); - $string = preg_replace('/[\"\']$/', "", $string); - } else { - $string = trim($string, "'\""); - } - - $string = str_replace(array("\\\n", '\\"', "\\'"), - array("", '"', "'"), $string); - - // Convert escaped hex characters into ascii characters (e.g. \A => newline) - $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", - function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); }, - $string); - return $string; - } - - /** - * Parses a CSS "quotes" property - * - * @return array|null An array of pairs of quotes - */ - protected function _parse_quotes() - { - // Matches quote types - $re = '/(\'[^\']*\')|(\"[^\"]*\")/'; - - $quotes = $this->_frame->get_style()->quotes; - - // split on spaces, except within quotes - if (!preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER)) { - return null; - } - - $quotes_array = array(); - foreach ($matches as $_quote) { - $quotes_array[] = $this->_parse_string($_quote[0], true); - } - - if (empty($quotes_array)) { - $quotes_array = array('"', '"'); - } - - return array_chunk($quotes_array, 2); - } - - /** - * Parses the CSS "content" property - * - * @return string|null The resulting string - */ - protected function _parse_content() - { - // Matches generated content - $re = "/\n" . - "\s(counters?\\([^)]*\\))|\n" . - "\A(counters?\\([^)]*\\))|\n" . - "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?_frame->get_style()->content; - - $quotes = $this->_parse_quotes(); - - // split on spaces, except within quotes - if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER)) { - return null; - } - - $text = ""; - - foreach ($matches as $match) { - if (isset($match[2]) && $match[2] !== "") { - $match[1] = $match[2]; - } - - if (isset($match[6]) && $match[6] !== "") { - $match[4] = $match[6]; - } - - if (isset($match[8]) && $match[8] !== "") { - $match[7] = $match[8]; - } - - if (isset($match[1]) && $match[1] !== "") { - // counters?(...) - $match[1] = mb_strtolower(trim($match[1])); - - // Handle counter() references: - // http://www.w3.org/TR/CSS21/generate.html#content - - $i = mb_strpos($match[1], ")"); - if ($i === false) { - continue; - } - - preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]*)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $match[1], $args); - $counter_id = $args[3]; - if (strtolower($args[1]) == 'counter') { - // counter(name [,style]) - if (isset($args[5])) { - $type = trim($args[5]); - } else { - $type = null; - } - $p = $this->_frame->lookup_counter_frame($counter_id); - - $text .= $p->counter_value($counter_id, $type); - - } else if (strtolower($args[1]) == 'counters') { - // counters(name, string [,style]) - if (isset($args[5])) { - $string = $this->_parse_string($args[5]); - } else { - $string = ""; - } - - if (isset($args[7])) { - $type = trim($args[7]); - } else { - $type = null; - } - - $p = $this->_frame->lookup_counter_frame($counter_id); - $tmp = array(); - while ($p) { - // We only want to use the counter values when they actually increment the counter - if (array_key_exists($counter_id, $p->_counters)) { - array_unshift($tmp, $p->counter_value($counter_id, $type)); - } - $p = $p->lookup_counter_frame($counter_id); - } - $text .= implode($string, $tmp); - } else { - // countertops? - continue; - } - - } else if (isset($match[4]) && $match[4] !== "") { - // String match - $text .= $this->_parse_string($match[4]); - } else if (isset($match[7]) && $match[7] !== "") { - // Directive match - - if ($match[7] === "open-quote") { - // FIXME: do something here - $text .= $quotes[0][0]; - } else if ($match[7] === "close-quote") { - // FIXME: do something else here - $text .= $quotes[0][1]; - } else if ($match[7] === "no-open-quote") { - // FIXME: - } else if ($match[7] === "no-close-quote") { - // FIXME: - } else if (mb_strpos($match[7], "attr(") === 0) { - $i = mb_strpos($match[7], ")"); - if ($i === false) { - continue; - } - - $attr = mb_substr($match[7], 5, $i - 5); - if ($attr == "") { - continue; - } - - $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr); - } else { - continue; - } - } - } - - return $text; - } - - /** - * Sets the generated content of a generated frame - */ - protected function _set_content() - { - $frame = $this->_frame; - $style = $frame->get_style(); - - // if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value - if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") { - $vars = preg_split('/\s+/', trim($reset), 2); - $frame->reset_counter($vars[0], (isset($frame->_counters['__' . $vars[0]]) ? $frame->_counters['__' . $vars[0]] : (isset($vars[1]) ? $vars[1] : 0))); - } - - if ($style->counter_increment && ($increment = $style->counter_increment) !== "none") { - $frame->increment_counters($increment); - } - - if ($style->content && $frame->get_node()->nodeName === "dompdf_generated") { - $content = $this->_parse_content(); - // add generated content to the font subset - // FIXME: This is currently too late because the font subset has already been generated. - // See notes in issue #750. - if ($frame->get_dompdf()->getOptions()->getIsFontSubsettingEnabled() && $frame->get_dompdf()->get_canvas() instanceof CPDF) { - $frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content); - } - - $node = $frame->get_node()->ownerDocument->createTextNode($content); - - $new_style = $style->get_stylesheet()->create_style(); - $new_style->inherit($style); - - $new_frame = new Frame($node); - $new_frame->set_style($new_style); - - Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root()); - $frame->append_child($new_frame); - } - } - - /** - * Determine current frame width based on contents - * - * @return float - */ - public function calculate_auto_width() - { - return $this->_frame->get_margin_width(); - } -} diff --git a/library/vendor/dompdf/src/FrameReflower/Block.php b/library/vendor/dompdf/src/FrameReflower/Block.php deleted file mode 100644 index c6b75eb01..000000000 --- a/library/vendor/dompdf/src/FrameReflower/Block.php +++ /dev/null @@ -1,948 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameReflower; - -use Dompdf\Frame; -use Dompdf\FrameDecorator\Block as BlockFrameDecorator; -use Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator; -use Dompdf\FrameDecorator\Text as TextFrameDecorator; -use Dompdf\Exception; -use Dompdf\Css\Style; - -/** - * Reflows block frames - * - * @package dompdf - */ -class Block extends AbstractFrameReflower -{ - // Minimum line width to justify, as fraction of available width - const MIN_JUSTIFY_WIDTH = 0.80; - - /** - * @var BlockFrameDecorator - */ - protected $_frame; - - function __construct(BlockFrameDecorator $frame) - { - parent::__construct($frame); - } - - /** - * Calculate the ideal used value for the width property as per: - * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins - * - * @param float $width - * - * @return array - */ - protected function _calculate_width($width) - { - $frame = $this->_frame; - $style = $frame->get_style(); - $w = $frame->get_containing_block("w"); - - if ($style->position === "fixed") { - $w = $frame->get_parent()->get_containing_block("w"); - } - - $rm = $style->length_in_pt($style->margin_right, $w); - $lm = $style->length_in_pt($style->margin_left, $w); - - $left = $style->length_in_pt($style->left, $w); - $right = $style->length_in_pt($style->right, $w); - - // Handle 'auto' values - $dims = array($style->border_left_width, - $style->border_right_width, - $style->padding_left, - $style->padding_right, - $width !== "auto" ? $width : 0, - $rm !== "auto" ? $rm : 0, - $lm !== "auto" ? $lm : 0); - - // absolutely positioned boxes take the 'left' and 'right' properties into account - if ($frame->is_absolute()) { - $absolute = true; - $dims[] = $left !== "auto" ? $left : 0; - $dims[] = $right !== "auto" ? $right : 0; - } else { - $absolute = false; - } - - $sum = (float)$style->length_in_pt($dims, $w); - - // Compare to the containing block - $diff = $w - $sum; - - if ($diff > 0) { - if ($absolute) { - // resolve auto properties: see - // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width - - if ($width === "auto" && $left === "auto" && $right === "auto") { - if ($lm === "auto") { - $lm = 0; - } - if ($rm === "auto") { - $rm = 0; - } - - // Technically, the width should be "shrink-to-fit" i.e. based on the - // preferred width of the content... a little too costly here as a - // special case. Just get the width to take up the slack: - $left = 0; - $right = 0; - $width = $diff; - } else if ($width === "auto") { - if ($lm === "auto") { - $lm = 0; - } - if ($rm === "auto") { - $rm = 0; - } - if ($left === "auto") { - $left = 0; - } - if ($right === "auto") { - $right = 0; - } - - $width = $diff; - } else if ($left === "auto") { - if ($lm === "auto") { - $lm = 0; - } - if ($rm === "auto") { - $rm = 0; - } - if ($right === "auto") { - $right = 0; - } - - $left = $diff; - } else if ($right === "auto") { - if ($lm === "auto") { - $lm = 0; - } - if ($rm === "auto") { - $rm = 0; - } - - $right = $diff; - } - - } else { - // Find auto properties and get them to take up the slack - if ($width === "auto") { - $width = $diff; - } else if ($lm === "auto" && $rm === "auto") { - $lm = $rm = round($diff / 2); - } else if ($lm === "auto") { - $lm = $diff; - } else if ($rm === "auto") { - $rm = $diff; - } - } - } else if ($diff < 0) { - // We are over constrained--set margin-right to the difference - $rm = $diff; - } - - return array( - "width" => $width, - "margin_left" => $lm, - "margin_right" => $rm, - "left" => $left, - "right" => $right, - ); - } - - /** - * Call the above function, but resolve max/min widths - * - * @throws Exception - * @return array - */ - protected function _calculate_restricted_width() - { - $frame = $this->_frame; - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - - if ($style->position === "fixed") { - $cb = $frame->get_root()->get_containing_block(); - } - - //if ( $style->position === "absolute" ) - // $cb = $frame->find_positionned_parent()->get_containing_block(); - - if (!isset($cb["w"])) { - throw new Exception("Box property calculation requires containing block width"); - } - - // Treat width 100% as auto - if ($style->width === "100%") { - $width = "auto"; - } else { - $width = $style->length_in_pt($style->width, $cb["w"]); - } - - $calculate_width = $this->_calculate_width($width); - $margin_left = $calculate_width['margin_left']; - $margin_right = $calculate_width['margin_right']; - $width = $calculate_width['width']; - $left = $calculate_width['left']; - $right = $calculate_width['right']; - - // Handle min/max width - $min_width = $style->length_in_pt($style->min_width, $cb["w"]); - $max_width = $style->length_in_pt($style->max_width, $cb["w"]); - - if ($max_width !== "none" && $min_width > $max_width) { - list($max_width, $min_width) = array($min_width, $max_width); - } - - if ($max_width !== "none" && $width > $max_width) { - extract($this->_calculate_width($max_width)); - } - - if ($width < $min_width) { - $calculate_width = $this->_calculate_width($min_width); - $margin_left = $calculate_width['margin_left']; - $margin_right = $calculate_width['margin_right']; - $width = $calculate_width['width']; - $left = $calculate_width['left']; - $right = $calculate_width['right']; - } - - return array($width, $margin_left, $margin_right, $left, $right); - } - - /** - * Determine the unrestricted height of content within the block - * not by adding each line's height, but by getting the last line's position. - * This because lines could have been pushed lower by a clearing element. - * - * @return float - */ - protected function _calculate_content_height() - { - $height = 0; - $lines = $this->_frame->get_line_boxes(); - if (count($lines) > 0) { - $last_line = end($lines); - $content_box = $this->_frame->get_content_box(); - $height = $last_line->y + $last_line->h - $content_box["y"]; - } - return $height; - } - - /** - * Determine the frame's restricted height - * - * @return array - */ - protected function _calculate_restricted_height() - { - $frame = $this->_frame; - $style = $frame->get_style(); - $content_height = $this->_calculate_content_height(); - $cb = $frame->get_containing_block(); - - $height = $style->length_in_pt($style->height, $cb["h"]); - - $top = $style->length_in_pt($style->top, $cb["h"]); - $bottom = $style->length_in_pt($style->bottom, $cb["h"]); - - $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]); - $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - if ($frame->is_absolute()) { - - // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height - - $dims = array($top !== "auto" ? $top : 0, - $style->margin_top !== "auto" ? $style->margin_top : 0, - $style->padding_top, - $style->border_top_width, - $height !== "auto" ? $height : 0, - $style->border_bottom_width, - $style->padding_bottom, - $style->margin_bottom !== "auto" ? $style->margin_bottom : 0, - $bottom !== "auto" ? $bottom : 0); - - $sum = (float)$style->length_in_pt($dims, $cb["h"]); - - $diff = $cb["h"] - $sum; - - if ($diff > 0) { - if ($height === "auto" && $top === "auto" && $bottom === "auto") { - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - - $height = $diff; - } else if ($height === "auto" && $top === "auto") { - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - - $height = $content_height; - $top = $diff - $content_height; - } else if ($height === "auto" && $bottom === "auto") { - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - - $height = $content_height; - $bottom = $diff - $content_height; - } else if ($top === "auto" && $bottom === "auto") { - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - - $bottom = $diff; - } else if ($top === "auto") { - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - - $top = $diff; - } else if ($height === "auto") { - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - - $height = $diff; - } else if ($bottom === "auto") { - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - - $bottom = $diff; - } else { - if ($style->overflow === "visible") { - // set all autos to zero - if ($margin_top === "auto") { - $margin_top = 0; - } - if ($margin_bottom === "auto") { - $margin_bottom = 0; - } - if ($top === "auto") { - $top = 0; - } - if ($bottom === "auto") { - $bottom = 0; - } - if ($height === "auto") { - $height = $content_height; - } - } - - // FIXME: overflow hidden - } - } - - } else { - // Expand the height if overflow is visible - if ($height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) { - $height = $content_height; - } - - // FIXME: this should probably be moved to a seperate function as per - // _calculate_restricted_width - - // Only handle min/max height if the height is independent of the frame's content - if (!($style->overflow === "visible" || ($style->overflow === "hidden" && $height === "auto"))) { - $min_height = $style->min_height; - $max_height = $style->max_height; - - if (isset($cb["h"])) { - $min_height = $style->length_in_pt($min_height, $cb["h"]); - $max_height = $style->length_in_pt($max_height, $cb["h"]); - } else if (isset($cb["w"])) { - if (mb_strpos($min_height, "%") !== false) { - $min_height = 0; - } else { - $min_height = $style->length_in_pt($min_height, $cb["w"]); - } - - if (mb_strpos($max_height, "%") !== false) { - $max_height = "none"; - } else { - $max_height = $style->length_in_pt($max_height, $cb["w"]); - } - } - - if ($max_height !== "none" && $min_height > $max_height) { - // Swap 'em - list($max_height, $min_height) = array($min_height, $max_height); - } - - if ($max_height !== "none" && $height > $max_height) { - $height = $max_height; - } - - if ($height < $min_height) { - $height = $min_height; - } - } - } - - return array($height, $margin_top, $margin_bottom, $top, $bottom); - } - - /** - * Adjust the justification of each of our lines. - * http://www.w3.org/TR/CSS21/text.html#propdef-text-align - */ - protected function _text_align() - { - $style = $this->_frame->get_style(); - $w = $this->_frame->get_containing_block("w"); - $width = (float)$style->length_in_pt($style->width, $w); - - switch ($style->text_align) { - default: - case "left": - foreach ($this->_frame->get_line_boxes() as $line) { - if (!$line->left) { - continue; - } - - foreach ($line->get_frames() as $frame) { - if ($frame instanceof BlockFrameDecorator) { - continue; - } - $frame->set_position($frame->get_position("x") + $line->left); - } - } - return; - - case "right": - foreach ($this->_frame->get_line_boxes() as $line) { - // Move each child over by $dx - $dx = $width - $line->w - $line->right; - - foreach ($line->get_frames() as $frame) { - // Block frames are not aligned by text-align - if ($frame instanceof BlockFrameDecorator) { - continue; - } - - $frame->set_position($frame->get_position("x") + $dx); - } - } - break; - - case "justify": - // We justify all lines except the last one - $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards) - $last_line = array_pop($lines); - - foreach ($lines as $i => $line) { - if ($line->br) { - unset($lines[$i]); - } - } - - // One space character's width. Will be used to get a more accurate spacing - $space_width = $this->get_dompdf()->getFontMetrics()->getTextWidth(" ", $style->font_family, $style->font_size); - - foreach ($lines as $line) { - if ($line->left) { - foreach ($line->get_frames() as $frame) { - if (!$frame instanceof TextFrameDecorator) { - continue; - } - - $frame->set_position($frame->get_position("x") + $line->left); - } - } - - // Set the spacing for each child - if ($line->wc > 1) { - $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1); - } else { - $spacing = 0; - } - - $dx = 0; - foreach ($line->get_frames() as $frame) { - if (!$frame instanceof TextFrameDecorator) { - continue; - } - - $text = $frame->get_text(); - $spaces = mb_substr_count($text, " "); - - $char_spacing = (float)$style->length_in_pt($style->letter_spacing); - $_spacing = $spacing + $char_spacing; - - $frame->set_position($frame->get_position("x") + $dx); - $frame->set_text_spacing($_spacing); - - $dx += $spaces * $_spacing; - } - - // The line (should) now occupy the entire width - $line->w = $width; - } - - // Adjust the last line if necessary - if ($last_line->left) { - foreach ($last_line->get_frames() as $frame) { - if ($frame instanceof BlockFrameDecorator) { - continue; - } - $frame->set_position($frame->get_position("x") + $last_line->left); - } - } - break; - - case "center": - case "centre": - foreach ($this->_frame->get_line_boxes() as $line) { - // Centre each line by moving each frame in the line by: - $dx = ($width + $line->left - $line->w - $line->right) / 2; - - foreach ($line->get_frames() as $frame) { - // Block frames are not aligned by text-align - if ($frame instanceof BlockFrameDecorator) { - continue; - } - - $frame->set_position($frame->get_position("x") + $dx); - } - } - break; - } - } - - /** - * Align inline children vertically. - * Aligns each child vertically after each line is reflowed - */ - function vertical_align() - { - $canvas = null; - - foreach ($this->_frame->get_line_boxes() as $line) { - - $height = $line->h; - - foreach ($line->get_frames() as $frame) { - $style = $frame->get_style(); - $isInlineBlock = ( - '-dompdf-image' === $style->display - || 'inline-block' === $style->display - || 'inline-table' === $style->display - ); - if (!$isInlineBlock && $style->display !== "inline") { - continue; - } - - if (!isset($canvas)) { - $canvas = $frame->get_root()->get_dompdf()->get_canvas(); - } - - $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size); - $y_offset = 0; - - //FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?) - if($isInlineBlock) { - $lineFrames = $line->get_frames(); - if (count($lineFrames) == 1) { - continue; - } - $frameBox = $frame->get_frame()->get_border_box(); - $imageHeightDiff = $height * 0.8 - (float)$frameBox['h']; - - $align = $frame->get_style()->vertical_align; - if (in_array($align, Style::$vertical_align_keywords) === true) { - switch ($align) { - case "middle": - $y_offset = $imageHeightDiff / 2; - break; - - case "sub": - $y_offset = 0.3 * $height + $imageHeightDiff; - break; - - case "super": - $y_offset = -0.2 * $height + $imageHeightDiff; - break; - - case "text-top": // FIXME: this should be the height of the frame minus the height of the text - $y_offset = $height - (float)$style->length_in_pt($style->line_height, $style->font_size); - break; - - case "top": - break; - - case "text-bottom": // FIXME: align bottom of image with the descender? - case "bottom": - $y_offset = 0.3 * $height + $imageHeightDiff; - break; - - case "baseline": - default: - $y_offset = $imageHeightDiff; - break; - } - } else { - $y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - (float)$frameBox['h']; - } - } else { - $parent = $frame->get_parent(); - if ($parent instanceof TableCellFrameDecorator) { - $align = "baseline"; - } else { - $align = $parent->get_style()->vertical_align; - } - if (in_array($align, Style::$vertical_align_keywords) === true) { - switch ($align) { - case "middle": - $y_offset = ($height * 0.8 - $baseline) / 2; - break; - - case "sub": - $y_offset = $height * 0.8 - $baseline * 0.5; - break; - - case "super": - $y_offset = $height * 0.8 - $baseline * 1.4; - break; - - case "text-top": - case "top": // Not strictly accurate, but good enough for now - break; - - case "text-bottom": - case "bottom": - $y_offset = $height * 0.8 - $baseline; - break; - - case "baseline": - default: - $y_offset = $height * 0.8 - $baseline; - break; - } - } else { - $y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size); - } - } - - if ($y_offset !== 0) { - $frame->move(0, $y_offset); - } - } - } - } - - /** - * @param Frame $child - */ - function process_clear(Frame $child) - { - $child_style = $child->get_style(); - $root = $this->_frame->get_root(); - - // Handle "clear" - if ($child_style->clear !== "none") { - //TODO: this is a WIP for handling clear/float frames that are in between inline frames - if ($child->get_prev_sibling() !== null) { - $this->_frame->add_line(); - } - if ($child_style->float !== "none" && $child->get_next_sibling()) { - $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1); - } - - $lowest_y = $root->get_lowest_float_offset($child); - - // If a float is still applying, we handle it - if ($lowest_y) { - if ($child->is_in_flow()) { - $line_box = $this->_frame->get_current_line_box(); - $line_box->y = $lowest_y + $child->get_margin_height(); - $line_box->left = 0; - $line_box->right = 0; - } - - $child->move(0, $lowest_y - $child->get_position("y")); - } - } - } - - /** - * @param Frame $child - * @param float $cb_x - * @param float $cb_w - */ - function process_float(Frame $child, $cb_x, $cb_w) - { - $child_style = $child->get_style(); - $root = $this->_frame->get_root(); - - // Handle "float" - if ($child_style->float !== "none") { - $root->add_floating_frame($child); - - // Remove next frame's beginning whitespace - $next = $child->get_next_sibling(); - if ($next && $next instanceof TextFrameDecorator) { - $next->set_text(ltrim($next->get_text())); - } - - $line_box = $this->_frame->get_current_line_box(); - list($old_x, $old_y) = $child->get_position(); - - $float_x = $cb_x; - $float_y = $old_y; - $float_w = $child->get_margin_width(); - - if ($child_style->clear === "none") { - switch ($child_style->float) { - case "left": - $float_x += $line_box->left; - break; - case "right": - $float_x += ($cb_w - $line_box->right - $float_w); - break; - } - } else { - if ($child_style->float === "right") { - $float_x += ($cb_w - $float_w); - } - } - - if ($cb_w < $float_x + $float_w - $old_x) { - // TODO handle when floating elements don't fit - } - - $line_box->get_float_offsets(); - - if ($child->_float_next_line) { - $float_y += $line_box->h; - } - - $child->set_position($float_x, $float_y); - $child->move($float_x - $old_x, $float_y - $old_y, true); - } - } - - /** - * @param BlockFrameDecorator $block - * @return mixed|void - */ - function reflow(BlockFrameDecorator $block = null) - { - - // Check if a page break is forced - $page = $this->_frame->get_root(); - $page->check_forced_page_break($this->_frame); - - // Bail if the page is full - if ($page->is_full()) { - return; - } - - // Generated content - $this->_set_content(); - - // Collapse margins if required - $this->_collapse_margins(); - - $style = $this->_frame->get_style(); - $cb = $this->_frame->get_containing_block(); - - if ($style->position === "fixed") { - $cb = $this->_frame->get_root()->get_containing_block(); - } - - // Determine the constraints imposed by this frame: calculate the width - // of the content area: - list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); - - // Store the calculated properties - $style->width = $w; - $style->margin_left = $left_margin; - $style->margin_right = $right_margin; - $style->left = $left; - $style->right = $right; - - // Update the position - $this->_frame->position(); - list($x, $y) = $this->_frame->get_position(); - - // Adjust the first line based on the text-indent property - $indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]); - $this->_frame->increase_line_width($indent); - - // Determine the content edge - $top = (float)$style->length_in_pt(array($style->margin_top, - $style->padding_top, - $style->border_top_width), $cb["h"]); - - $bottom = (float)$style->length_in_pt(array($style->border_bottom_width, - $style->margin_bottom, - $style->padding_bottom), $cb["h"]); - - $cb_x = $x + (float)$left_margin + (float)$style->length_in_pt(array($style->border_left_width, - $style->padding_left), $cb["w"]); - - $cb_y = $y + $top; - - $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y; - - // Set the y position of the first line in this block - $line_box = $this->_frame->get_current_line_box(); - $line_box->y = $cb_y; - $line_box->get_float_offsets(); - - // Set the containing blocks and reflow each child - foreach ($this->_frame->get_children() as $child) { - - // Bail out if the page is full - if ($page->is_full()) { - break; - } - - $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); - - $this->process_clear($child); - - $child->reflow($this->_frame); - - // Don't add the child to the line if a page break has occurred - if ($page->check_page_break($child)) { - break; - } - - $this->process_float($child, $cb_x, $w); - } - - // Determine our height - list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); - $style->height = $height; - $style->margin_top = $margin_top; - $style->margin_bottom = $margin_bottom; - $style->top = $top; - $style->bottom = $bottom; - - $orig_style = $this->_frame->get_original_style(); - - $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto")); - - // Absolute positioning measurement - if ($needs_reposition) { - if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) { - $width = 0; - foreach ($this->_frame->get_line_boxes() as $line) { - $width = max($line->w, $width); - } - $style->width = $width; - } - - $style->left = $orig_style->left; - $style->right = $orig_style->right; - } - - // Calculate inline-block / float auto-widths - if (($style->display === "inline-block" || $style->float !== 'none') && $orig_style->width === 'auto') { - $width = 0; - - foreach ($this->_frame->get_line_boxes() as $line) { - $line->recalculate_width(); - - $width = max($line->w, $width); - } - - if ($width === 0) { - foreach ($this->_frame->get_children() as $child) { - $width += $child->calculate_auto_width(); - } - } - - $style->width = $width; - } - - $this->_text_align(); - $this->vertical_align(); - - // Absolute positioning - if ($needs_reposition) { - list($x, $y) = $this->_frame->get_position(); - $this->_frame->position(); - list($new_x, $new_y) = $this->_frame->get_position(); - $this->_frame->move($new_x - $x, $new_y - $y, true); - } - - if ($block && $this->_frame->is_in_flow()) { - $block->add_frame_to_line($this->_frame); - - // May be inline-block - if ($style->display === "block") { - $block->add_line(); - } - } - } - - /** - * Determine current frame width based on contents - * - * @return float - */ - public function calculate_auto_width() - { - $width = 0; - - foreach ($this->_frame->get_line_boxes() as $line) { - $line_width = 0; - - foreach ($line->get_frames() as $frame) { - if ($frame->get_original_style()->width == 'auto') { - $line_width += $frame->calculate_auto_width(); - } else { - $line_width += $frame->get_margin_width(); - } - } - - $width = max($line_width, $width); - } - - $this->_frame->get_style()->width = $width; - - return $this->_frame->get_margin_width(); - } -} diff --git a/library/vendor/dompdf/src/FrameReflower/Image.php b/library/vendor/dompdf/src/FrameReflower/Image.php deleted file mode 100644 index 805523c52..000000000 --- a/library/vendor/dompdf/src/FrameReflower/Image.php +++ /dev/null @@ -1,206 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameReflower; - -use Dompdf\Helpers; -use Dompdf\FrameDecorator\Block as BlockFrameDecorator; -use Dompdf\FrameDecorator\Image as ImageFrameDecorator; - -/** - * Image reflower class - * - * @package dompdf - */ -class Image extends AbstractFrameReflower -{ - - /** - * Image constructor. - * @param ImageFrameDecorator $frame - */ - function __construct(ImageFrameDecorator $frame) - { - parent::__construct($frame); - } - - /** - * @param BlockFrameDecorator|null $block - */ - function reflow(BlockFrameDecorator $block = null) - { - $this->_frame->position(); - - //FLOAT - //$frame = $this->_frame; - //$page = $frame->get_root(); - - //if ($frame->get_style()->float !== "none" ) { - // $page->add_floating_frame($this); - //} - - // Set the frame's width - $this->get_min_max_width(); - - if ($block) { - $block->add_frame_to_line($this->_frame); - } - } - - /** - * @return array - */ - function get_min_max_width() - { - if ($this->get_dompdf()->getOptions()->getDebugPng()) { - // Determine the image's size. Time consuming. Only when really needed? - list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext()); - print "get_min_max_width() " . - $this->_frame->get_style()->width . ' ' . - $this->_frame->get_style()->height . ';' . - $this->_frame->get_parent()->get_style()->width . " " . - $this->_frame->get_parent()->get_style()->height . ";" . - $this->_frame->get_parent()->get_parent()->get_style()->width . ' ' . - $this->_frame->get_parent()->get_parent()->get_style()->height . ';' . - $img_width . ' ' . - $img_height . '|'; - } - - $style = $this->_frame->get_style(); - - $width_forced = true; - $height_forced = true; - - //own style auto or invalid value: use natural size in px - //own style value: ignore suffix text including unit, use given number as px - //own style %: walk up parent chain until found available space in pt; fill available space - // - //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto - - $width = ($style->width > 0 ? $style->width : 0); - if (Helpers::is_percent($width)) { - $t = 0.0; - for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { - $f_style = $f->get_style(); - $t = $f_style->length_in_pt($f_style->width); - if ($t != 0) { - break; - } - } - $width = ((float)rtrim($width, "%") * $t) / 100; //maybe 0 - } else { - // Don't set image original size if "%" branch was 0 or size not given. - // Otherwise aspect changed on %/auto combination for width/height - // Resample according to px per inch - // See also ListBulletImage::__construct - $width = $style->length_in_pt($width); - } - - $height = ($style->height > 0 ? $style->height : 0); - if (Helpers::is_percent($height)) { - $t = 0.0; - for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { - $f_style = $f->get_style(); - $t = (float)$f_style->length_in_pt($f_style->height); - if ($t != 0) { - break; - } - } - $height = ((float)rtrim($height, "%") * $t) / 100; //maybe 0 - } else { - // Don't set image original size if "%" branch was 0 or size not given. - // Otherwise aspect changed on %/auto combination for width/height - // Resample according to px per inch - // See also ListBulletImage::__construct - $height = $style->length_in_pt($height); - } - - if ($width == 0 || $height == 0) { - // Determine the image's size. Time consuming. Only when really needed! - list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext()); - - // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable. - // Resample according to px per inch - // See also ListBulletImage::__construct - if ($width == 0 && $height == 0) { - $dpi = $this->_frame->get_dompdf()->getOptions()->getDpi(); - $width = (float)($img_width * 72) / $dpi; - $height = (float)($img_height * 72) / $dpi; - $width_forced = false; - $height_forced = false; - } elseif ($height == 0 && $width != 0) { - $height_forced = false; - $height = ($width / $img_width) * $img_height; //keep aspect ratio - } elseif ($width == 0 && $height != 0) { - $width_forced = false; - $width = ($height / $img_height) * $img_width; //keep aspect ratio - } - } - - // Handle min/max width/height - if ($style->min_width !== "none" || - $style->max_width !== "none" || - $style->min_height !== "none" || - $style->max_height !== "none" - ) { - - list( /*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block(); - - $min_width = $style->length_in_pt($style->min_width, $w); - $max_width = $style->length_in_pt($style->max_width, $w); - $min_height = $style->length_in_pt($style->min_height, $h); - $max_height = $style->length_in_pt($style->max_height, $h); - - if ($max_width !== "none" && $width > $max_width) { - if (!$height_forced) { - $height *= $max_width / $width; - } - - $width = $max_width; - } - - if ($min_width !== "none" && $width < $min_width) { - if (!$height_forced) { - $height *= $min_width / $width; - } - - $width = $min_width; - } - - if ($max_height !== "none" && $height > $max_height) { - if (!$width_forced) { - $width *= $max_height / $height; - } - - $height = $max_height; - } - - if ($min_height !== "none" && $height < $min_height) { - if (!$width_forced) { - $width *= $min_height / $height; - } - - $height = $min_height; - } - } - - if ($this->get_dompdf()->getOptions()->getDebugPng()) { - print $width . ' ' . $height . ';'; - } - - $style->width = $width . "pt"; - $style->height = $height . "pt"; - - $style->min_width = "none"; - $style->max_width = "none"; - $style->min_height = "none"; - $style->max_height = "none"; - - return array($width, $width, "min" => $width, "max" => $width); - } -} diff --git a/library/vendor/dompdf/src/FrameReflower/Inline.php b/library/vendor/dompdf/src/FrameReflower/Inline.php deleted file mode 100644 index 68662a574..000000000 --- a/library/vendor/dompdf/src/FrameReflower/Inline.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameReflower; - -use Dompdf\Frame; -use Dompdf\FrameDecorator\Block as BlockFrameDecorator; -use Dompdf\FrameDecorator\Text as TextFrameDecorator; - -/** - * Reflows inline frames - * - * @package dompdf - */ -class Inline extends AbstractFrameReflower -{ - - /** - * Inline constructor. - * @param Frame $frame - */ - function __construct(Frame $frame) - { - parent::__construct($frame); - } - - /** - * @param BlockFrameDecorator|null $block - */ - function reflow(BlockFrameDecorator $block = null) - { - $frame = $this->_frame; - - // Check if a page break is forced - $page = $frame->get_root(); - $page->check_forced_page_break($frame); - - if ($page->is_full()) { - return; - } - - $style = $frame->get_style(); - - // Generated content - $this->_set_content(); - - $frame->position(); - - $cb = $frame->get_containing_block(); - - // Add our margin, padding & border to the first and last children - if (($f = $frame->get_first_child()) && $f instanceof TextFrameDecorator) { - $f_style = $f->get_style(); - $f_style->margin_left = $style->margin_left; - $f_style->padding_left = $style->padding_left; - $f_style->border_left = $style->border_left; - } - - if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) { - $l_style = $l->get_style(); - $l_style->margin_right = $style->margin_right; - $l_style->padding_right = $style->padding_right; - $l_style->border_right = $style->border_right; - } - - if ($block) { - $block->add_frame_to_line($this->_frame); - } - - // Set the containing blocks and reflow each child. The containing - // block is not changed by line boxes. - foreach ($frame->get_children() as $child) { - $child->set_containing_block($cb); - $child->reflow($block); - } - } - - /** - * Determine current frame width based on contents - * - * @return float - */ - public function calculate_auto_width() - { - $width = 0; - - foreach ($this->_frame->get_children() as $child) { - if ($child->get_original_style()->width == 'auto') { - $width += $child->calculate_auto_width(); - } else { - $width += $child->get_margin_width(); - } - } - - $this->_frame->get_style()->width = $width; - - return $this->_frame->get_margin_width(); - } -} diff --git a/library/vendor/dompdf/src/FrameReflower/Table.php b/library/vendor/dompdf/src/FrameReflower/Table.php deleted file mode 100644 index 1f560c053..000000000 --- a/library/vendor/dompdf/src/FrameReflower/Table.php +++ /dev/null @@ -1,589 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameReflower; - -use Dompdf\FrameDecorator\Block as BlockFrameDecorator; -use Dompdf\FrameDecorator\Table as TableFrameDecorator; - -/** - * Reflows tables - * - * @access private - * @package dompdf - */ -class Table extends AbstractFrameReflower -{ - /** - * Frame for this reflower - * - * @var TableFrameDecorator - */ - protected $_frame; - - /** - * Cache of results between call to get_min_max_width and assign_widths - * - * @var array - */ - protected $_state; - - /** - * Table constructor. - * @param TableFrameDecorator $frame - */ - function __construct(TableFrameDecorator $frame) - { - $this->_state = null; - parent::__construct($frame); - } - - /** - * State is held here so it needs to be reset along with the decorator - */ - function reset() - { - $this->_state = null; - $this->_min_max_cache = null; - } - - protected function _assign_widths() - { - $style = $this->_frame->get_style(); - - // Find the min/max width of the table and sort the columns into - // absolute/percent/auto arrays - $min_width = $this->_state["min_width"]; - $max_width = $this->_state["max_width"]; - $percent_used = $this->_state["percent_used"]; - $absolute_used = $this->_state["absolute_used"]; - $auto_min = $this->_state["auto_min"]; - - $absolute =& $this->_state["absolute"]; - $percent =& $this->_state["percent"]; - $auto =& $this->_state["auto"]; - - // Determine the actual width of the table - $cb = $this->_frame->get_containing_block(); - $columns =& $this->_frame->get_cellmap()->get_columns(); - - $width = $style->width; - - // Calculate padding & border fudge factor - $left = $style->margin_left; - $right = $style->margin_right; - - $centered = ($left === "auto" && $right === "auto"); - - $left = (float)($left === "auto" ? 0 : $style->length_in_pt($left, $cb["w"])); - $right = (float)($right === "auto" ? 0 : $style->length_in_pt($right, $cb["w"])); - - $delta = $left + $right; - - if (!$centered) { - $delta += (float)$style->length_in_pt(array( - $style->padding_left, - $style->border_left_width, - $style->border_right_width, - $style->padding_right), - $cb["w"]); - } - - $min_table_width = (float)$style->length_in_pt($style->min_width, $cb["w"] - $delta); - - // min & max widths already include borders & padding - $min_width -= $delta; - $max_width -= $delta; - - if ($width !== "auto") { - $preferred_width = (float)$style->length_in_pt($width, $cb["w"]) - $delta; - - if ($preferred_width < $min_table_width) { - $preferred_width = $min_table_width; - } - - if ($preferred_width > $min_width) { - $width = $preferred_width; - } else { - $width = $min_width; - } - - } else { - if ($max_width + $delta < $cb["w"]) { - $width = $max_width; - } else if ($cb["w"] - $delta > $min_width) { - $width = $cb["w"] - $delta; - } else { - $width = $min_width; - } - - if ($width < $min_table_width) { - $width = $min_table_width; - } - - } - - // Store our resolved width - $style->width = $width; - - $cellmap = $this->_frame->get_cellmap(); - - if ($cellmap->is_columns_locked()) { - return; - } - - // If the whole table fits on the page, then assign each column it's max width - if ($width == $max_width) { - foreach (array_keys($columns) as $i) { - $cellmap->set_column_width($i, $columns[$i]["max-width"]); - } - - return; - } - - // Determine leftover and assign it evenly to all columns - if ($width > $min_width) { - // We have four cases to deal with: - // - // 1. All columns are auto--no widths have been specified. In this - // case we distribute extra space across all columns weighted by max-width. - // - // 2. Only absolute widths have been specified. In this case we - // distribute any extra space equally among 'width: auto' columns, or all - // columns if no auto columns have been specified. - // - // 3. Only percentage widths have been specified. In this case we - // normalize the percentage values and distribute any remaining % to - // width: auto columns. We then proceed to assign widths as fractions - // of the table width. - // - // 4. Both absolute and percentage widths have been specified. - - $increment = 0; - - // Case 1: - if ($absolute_used == 0 && $percent_used == 0) { - $increment = $width - $min_width; - - foreach (array_keys($columns) as $i) { - $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width)); - } - return; - } - - // Case 2 - if ($absolute_used > 0 && $percent_used == 0) { - if (count($auto) > 0) { - $increment = ($width - $auto_min - $absolute_used) / count($auto); - } - - // Use the absolutely specified width or the increment - foreach (array_keys($columns) as $i) { - if ($columns[$i]["absolute"] > 0 && count($auto)) { - $cellmap->set_column_width($i, $columns[$i]["min-width"]); - } else if (count($auto)) { - $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); - } else { - // All absolute columns - $increment = ($width - $absolute_used) * $columns[$i]["absolute"] / $absolute_used; - - $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); - } - - } - return; - } - - // Case 3: - if ($absolute_used == 0 && $percent_used > 0) { - $scale = null; - $remaining = null; - - // Scale percent values if the total percentage is > 100, or if all - // values are specified as percentages. - if ($percent_used > 100 || count($auto) == 0) { - $scale = 100 / $percent_used; - } else { - $scale = 1; - } - - // Account for the minimum space used by the unassigned auto columns - $used_width = $auto_min; - - foreach ($percent as $i) { - $columns[$i]["percent"] *= $scale; - - $slack = $width - $used_width; - - $w = min($columns[$i]["percent"] * $width / 100, $slack); - - if ($w < $columns[$i]["min-width"]) { - $w = $columns[$i]["min-width"]; - } - - $cellmap->set_column_width($i, $w); - $used_width += $w; - - } - - // This works because $used_width includes the min-width of each - // unassigned column - if (count($auto) > 0) { - $increment = ($width - $used_width) / count($auto); - - foreach ($auto as $i) { - $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); - } - - } - return; - } - - // Case 4: - - // First-come, first served - if ($absolute_used > 0 && $percent_used > 0) { - $used_width = $auto_min; - - foreach ($absolute as $i) { - $cellmap->set_column_width($i, $columns[$i]["min-width"]); - $used_width += $columns[$i]["min-width"]; - } - - // Scale percent values if the total percentage is > 100 or there - // are no auto values to take up slack - if ($percent_used > 100 || count($auto) == 0) { - $scale = 100 / $percent_used; - } else { - $scale = 1; - } - - $remaining_width = $width - $used_width; - - foreach ($percent as $i) { - $slack = $remaining_width - $used_width; - - $columns[$i]["percent"] *= $scale; - $w = min($columns[$i]["percent"] * $remaining_width / 100, $slack); - - if ($w < $columns[$i]["min-width"]) { - $w = $columns[$i]["min-width"]; - } - - $columns[$i]["used-width"] = $w; - $used_width += $w; - } - - if (count($auto) > 0) { - $increment = ($width - $used_width) / count($auto); - - foreach ($auto as $i) { - $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); - } - } - - return; - } - } else { // we are over constrained - // Each column gets its minimum width - foreach (array_keys($columns) as $i) { - $cellmap->set_column_width($i, $columns[$i]["min-width"]); - } - } - } - - /** - * Determine the frame's height based on min/max height - * - * @return float|int|mixed|string - */ - protected function _calculate_height() - { - $style = $this->_frame->get_style(); - $height = $style->height; - - $cellmap = $this->_frame->get_cellmap(); - $cellmap->assign_frame_heights(); - $rows = $cellmap->get_rows(); - - // Determine our content height - $content_height = 0; - foreach ($rows as $r) { - $content_height += $r["height"]; - } - - $cb = $this->_frame->get_containing_block(); - - if (!($style->overflow === "visible" || - ($style->overflow === "hidden" && $height === "auto")) - ) { - // Only handle min/max height if the height is independent of the frame's content - - $min_height = $style->min_height; - $max_height = $style->max_height; - - if (isset($cb["h"])) { - $min_height = $style->length_in_pt($min_height, $cb["h"]); - $max_height = $style->length_in_pt($max_height, $cb["h"]); - - } else if (isset($cb["w"])) { - if (mb_strpos($min_height, "%") !== false) { - $min_height = 0; - } else { - $min_height = $style->length_in_pt($min_height, $cb["w"]); - } - if (mb_strpos($max_height, "%") !== false) { - $max_height = "none"; - } else { - $max_height = $style->length_in_pt($max_height, $cb["w"]); - } - } - - if ($max_height !== "none" && $min_height > $max_height) { - // Swap 'em - list($max_height, $min_height) = array($min_height, $max_height); - } - - if ($max_height !== "none" && $height > $max_height) { - $height = $max_height; - } - - if ($height < $min_height) { - $height = $min_height; - } - } else { - // Use the content height or the height value, whichever is greater - if ($height !== "auto") { - $height = $style->length_in_pt($height, $cb["h"]); - - if ($height <= $content_height) { - $height = $content_height; - } else { - $cellmap->set_frame_heights($height, $content_height); - } - } else { - $height = $content_height; - } - } - - return $height; - } - - /** - * @param BlockFrameDecorator $block - */ - function reflow(BlockFrameDecorator $block = null) - { - /** @var TableFrameDecorator */ - $frame = $this->_frame; - - // Check if a page break is forced - $page = $frame->get_root(); - $page->check_forced_page_break($frame); - - // Bail if the page is full - if ($page->is_full()) { - return; - } - - // Let the page know that we're reflowing a table so that splits - // are suppressed (simply setting page-break-inside: avoid won't - // work because we may have an arbitrary number of block elements - // inside tds.) - $page->table_reflow_start(); - - // Collapse vertical margins, if required - $this->_collapse_margins(); - - $frame->position(); - - // Table layout algorithm: - // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout - - if (is_null($this->_state)) { - $this->get_min_max_width(); - } - - $cb = $frame->get_containing_block(); - $style = $frame->get_style(); - - // This is slightly inexact, but should be okay. Add half the - // border-spacing to the table as padding. The other half is added to - // the cells themselves. - if ($style->border_collapse === "separate") { - list($h, $v) = $style->border_spacing; - - $v = (float)$style->length_in_pt($v) / 2; - $h = (float)$style->length_in_pt($h) / 2; - - $style->padding_left = (float)$style->length_in_pt($style->padding_left, $cb["w"]) + $h; - $style->padding_right = (float)$style->length_in_pt($style->padding_right, $cb["w"]) + $h; - $style->padding_top = (float)$style->length_in_pt($style->padding_top, $cb["h"]) + $v; - $style->padding_bottom = (float)$style->length_in_pt($style->padding_bottom, $cb["h"]) + $v; - } - - $this->_assign_widths(); - - // Adjust left & right margins, if they are auto - $width = $style->width; - $left = $style->margin_left; - $right = $style->margin_right; - - $diff = $cb["w"] - $width; - - if ($left === "auto" && $right === "auto") { - if ($diff < 0) { - $left = 0; - $right = $diff; - } else { - $left = $right = $diff / 2; - } - - $style->margin_left = sprintf("%Fpt", $left); - $style->margin_right = sprintf("%Fpt", $right);; - } else { - if ($left === "auto") { - $left = (float)$style->length_in_pt($cb["w"], $cb["w"]) - (float)$style->length_in_pt($right, $cb["w"]) - (float)$style->length_in_pt($width, $cb["w"]); - } - if ($right === "auto") { - $left = (float)$style->length_in_pt($left, $cb["w"]); - } - } - - list($x, $y) = $frame->get_position(); - - // Determine the content edge - $content_x = $x + (float)$left + (float)$style->length_in_pt(array($style->padding_left, - $style->border_left_width), $cb["w"]); - $content_y = $y + (float)$style->length_in_pt(array($style->margin_top, - $style->border_top_width, - $style->padding_top), $cb["h"]); - - if (isset($cb["h"])) { - $h = $cb["h"]; - } else { - $h = null; - } - - $cellmap = $frame->get_cellmap(); - $col =& $cellmap->get_column(0); - $col["x"] = $content_x; - - $row =& $cellmap->get_row(0); - $row["y"] = $content_y; - - $cellmap->assign_x_positions(); - - // Set the containing block of each child & reflow - foreach ($frame->get_children() as $child) { - // Bail if the page is full - if (!$page->in_nested_table() && $page->is_full()) { - break; - } - - $child->set_containing_block($content_x, $content_y, $width, $h); - $child->reflow(); - - if (!$page->in_nested_table()) { - // Check if a split has occured - $page->check_page_break($child); - } - - } - - // Assign heights to our cells: - $style->height = $this->_calculate_height(); - - if ($style->border_collapse === "collapse") { - // Unset our borders because our cells are now using them - $style->border_style = "none"; - } - - $page->table_reflow_end(); - - // Debugging: - //echo ($this->_frame->get_cellmap()); - - if ($block && $style->float === "none" && $frame->is_in_flow()) { - $block->add_frame_to_line($frame); - $block->add_line(); - } - } - - /** - * @return array|null - */ - function get_min_max_width() - { - if (!is_null($this->_min_max_cache)) { - return $this->_min_max_cache; - } - - $style = $this->_frame->get_style(); - - $this->_frame->normalise(); - - // Add the cells to the cellmap (this will calcluate column widths as - // frames are added) - $this->_frame->get_cellmap()->add_frame($this->_frame); - - // Find the min/max width of the table and sort the columns into - // absolute/percent/auto arrays - $this->_state = array(); - $this->_state["min_width"] = 0; - $this->_state["max_width"] = 0; - - $this->_state["percent_used"] = 0; - $this->_state["absolute_used"] = 0; - $this->_state["auto_min"] = 0; - - $this->_state["absolute"] = array(); - $this->_state["percent"] = array(); - $this->_state["auto"] = array(); - - $columns =& $this->_frame->get_cellmap()->get_columns(); - foreach (array_keys($columns) as $i) { - $this->_state["min_width"] += $columns[$i]["min-width"]; - $this->_state["max_width"] += $columns[$i]["max-width"]; - - if ($columns[$i]["absolute"] > 0) { - $this->_state["absolute"][] = $i; - $this->_state["absolute_used"] += $columns[$i]["absolute"]; - } else if ($columns[$i]["percent"] > 0) { - $this->_state["percent"][] = $i; - $this->_state["percent_used"] += $columns[$i]["percent"]; - } else { - $this->_state["auto"][] = $i; - $this->_state["auto_min"] += $columns[$i]["min-width"]; - } - } - - // Account for margins & padding - $dims = array($style->border_left_width, - $style->border_right_width, - $style->padding_left, - $style->padding_right, - $style->margin_left, - $style->margin_right); - - if ($style->border_collapse !== "collapse") { - list($dims[]) = $style->border_spacing; - } - - $delta = (float)$style->length_in_pt($dims, $this->_frame->get_containing_block("w")); - - $this->_state["min_width"] += $delta; - $this->_state["max_width"] += $delta; - - return $this->_min_max_cache = array( - $this->_state["min_width"], - $this->_state["max_width"], - "min" => $this->_state["min_width"], - "max" => $this->_state["max_width"], - ); - } -} diff --git a/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php b/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php deleted file mode 100644 index 13a198745..000000000 --- a/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php +++ /dev/null @@ -1,72 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameReflower; - -use Dompdf\FrameDecorator\Block as BlockFrameDecorator; -use Dompdf\FrameDecorator\Table as TableFrameDecorator; - -/** - * Reflows table row groups (e.g. tbody tags) - * - * @package dompdf - */ -class TableRowGroup extends AbstractFrameReflower -{ - - /** - * TableRowGroup constructor. - * @param \Dompdf\Frame $frame - */ - function __construct($frame) - { - parent::__construct($frame); - } - - /** - * @param BlockFrameDecorator|null $block - */ - function reflow(BlockFrameDecorator $block = null) - { - $page = $this->_frame->get_root(); - - $style = $this->_frame->get_style(); - - // Our width is equal to the width of our parent table - $table = TableFrameDecorator::find_parent_table($this->_frame); - - $cb = $this->_frame->get_containing_block(); - - foreach ($this->_frame->get_children() as $child) { - // Bail if the page is full - if ($page->is_full()) { - return; - } - - $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]); - $child->reflow(); - - // Check if a split has occured - $page->check_page_break($child); - } - - if ($page->is_full()) { - return; - } - - $cellmap = $table->get_cellmap(); - $style->width = $cellmap->get_frame_width($this->_frame); - $style->height = $cellmap->get_frame_height($this->_frame); - - $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); - - if ($table->get_style()->border_collapse === "collapse") { - // Unset our borders because our cells are now using them - $style->border_style = "none"; - } - } -} diff --git a/library/vendor/dompdf/src/FrameReflower/Text.php b/library/vendor/dompdf/src/FrameReflower/Text.php deleted file mode 100644 index 321efeba5..000000000 --- a/library/vendor/dompdf/src/FrameReflower/Text.php +++ /dev/null @@ -1,511 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\FrameReflower; - -use Dompdf\FrameDecorator\Block as BlockFrameDecorator; -use Dompdf\FrameDecorator\Text as TextFrameDecorator; -use Dompdf\FontMetrics; -use Dompdf\Helpers; - -/** - * Reflows text frames. - * - * @package dompdf - */ -class Text extends AbstractFrameReflower -{ - - /** - * @var BlockFrameDecorator - */ - protected $_block_parent; // Nearest block-level ancestor - - /** - * @var TextFrameDecorator - */ - protected $_frame; - - public static $_whitespace_pattern = "/[ \t\r\n\f]+/u"; - - /** - * @var FontMetrics - */ - private $fontMetrics; - - /** - * @param TextFrameDecorator $frame - * @param FontMetrics $fontMetrics - */ - public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics) - { - parent::__construct($frame); - $this->setFontMetrics($fontMetrics); - } - - /** - * @param $text - * @return mixed - */ - protected function _collapse_white_space($text) - { - //$text = $this->_frame->get_text(); -// if ( $this->_block_parent->get_current_line_box->w == 0 ) -// $text = ltrim($text, " \n\r\t"); - return preg_replace(self::$_whitespace_pattern, " ", $text); - } - - /** - * @param $text - * @return bool|int - */ - protected function _line_break($text) - { - $style = $this->_frame->get_style(); - $size = $style->font_size; - $font = $style->font_family; - $current_line = $this->_block_parent->get_current_line_box(); - - // Determine the available width - $line_width = $this->_frame->get_containing_block("w"); - $current_line_width = $current_line->left + $current_line->w + $current_line->right; - - $available_width = $line_width - $current_line_width; - - // Account for word-spacing - $word_spacing = (float)$style->length_in_pt($style->word_spacing); - $char_spacing = (float)$style->length_in_pt($style->letter_spacing); - - // Determine the frame width including margin, padding & border - $text_width = $this->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); - $mbp_width = - (float)$style->length_in_pt(array($style->margin_left, - $style->border_left_width, - $style->padding_left, - $style->padding_right, - $style->border_right_width, - $style->margin_right), $line_width); - - $frame_width = $text_width + $mbp_width; - -// Debugging: -// Helpers::pre_r("Text: '" . htmlspecialchars($text). "'"); -// Helpers::pre_r("width: " .$frame_width); -// Helpers::pre_r("textwidth + delta: $text_width + $mbp_width"); -// Helpers::pre_r("font-size: $size"); -// Helpers::pre_r("cb[w]: " .$line_width); -// Helpers::pre_r("available width: " . $available_width); -// Helpers::pre_r("current line width: " . $current_line_width); - -// Helpers::pre_r($words); - - if ($frame_width <= $available_width) { - return false; - } - - // split the text into words - $words = preg_split('/([\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - $wc = count($words); - - // Determine the split point - $width = 0; - $str = ""; - reset($words); - - // @todo support , - for ($i = 0; $i < $wc; $i += 2) { - $word = $words[$i] . (isset($words[$i + 1]) ? $words[$i + 1] : ""); - $word_width = $this->getFontMetrics()->getTextWidth($word, $font, $size, $word_spacing, $char_spacing); - if ($width + $word_width + $mbp_width > $available_width) { - break; - } - - $width += $word_width; - $str .= $word; - } - - $break_word = ($style->word_wrap === "break-word"); - - // The first word has overflowed. Force it onto the line - if ($current_line_width == 0 && $width == 0) { - $s = ""; - $last_width = 0; - - if ($break_word) { - for ($j = 0; $j < strlen($word); $j++) { - $s .= $word[$j]; - $_width = $this->getFontMetrics()->getTextWidth($s, $font, $size, $word_spacing, $char_spacing); - if ($_width > $available_width) { - break; - } - - $last_width = $_width; - } - } - - if ($break_word && $last_width > 0) { - //$width += $last_width; - $str .= substr($s, 0, -1); - } else { - //$width += $word_width; - $str .= $word; - } - } - - $offset = mb_strlen($str); - - // More debugging: - // var_dump($str); - // print_r("Width: ". $width); - // print_r("Offset: " . $offset); - - return $offset; - } - - //........................................................................ - - /** - * @param $text - * @return bool|int - */ - protected function _newline_break($text) - { - if (($i = mb_strpos($text, "\n")) === false) { - return false; - } - - return $i + 1; - } - - /** - * - */ - protected function _layout_line() - { - $frame = $this->_frame; - $style = $frame->get_style(); - $text = $frame->get_text(); - $size = $style->font_size; - $font = $style->font_family; - - // Determine the text height - $style->height = $this->getFontMetrics()->getFontHeight($font, $size); - - $split = false; - $add_line = false; - - // Handle text transform: - // http://www.w3.org/TR/CSS21/text.html#propdef-text-transform - switch (strtolower($style->text_transform)) { - default: - break; - case "capitalize": - $text = Helpers::mb_ucwords($text); - break; - case "uppercase": - $text = mb_convert_case($text, MB_CASE_UPPER); - break; - case "lowercase": - $text = mb_convert_case($text, MB_CASE_LOWER); - break; - } - - // Handle white-space property: - // http://www.w3.org/TR/CSS21/text.html#propdef-white-space - switch ($style->white_space) { - default: - case "normal": - $frame->set_text($text = $this->_collapse_white_space($text)); - if ($text == "") { - break; - } - - $split = $this->_line_break($text); - break; - - case "pre": - $split = $this->_newline_break($text); - $add_line = $split !== false; - break; - - case "nowrap": - $frame->set_text($text = $this->_collapse_white_space($text)); - break; - - case "pre-wrap": - $split = $this->_newline_break($text); - - if (($tmp = $this->_line_break($text)) !== false) { - $add_line = $split < $tmp; - $split = min($tmp, $split); - } else - $add_line = true; - - break; - - case "pre-line": - // Collapse white-space except for \n - $frame->set_text($text = preg_replace("/[ \t]+/u", " ", $text)); - - if ($text == "") { - break; - } - - $split = $this->_newline_break($text); - - if (($tmp = $this->_line_break($text)) !== false) { - $add_line = $split < $tmp; - $split = min($tmp, $split); - } else { - $add_line = true; - } - - break; - - } - - // Handle degenerate case - if ($text === "") { - return; - } - - if ($split !== false) { - // Handle edge cases - if ($split == 0 && $text === " ") { - $frame->set_text(""); - return; - } - - if ($split == 0) { - // Trim newlines from the beginning of the line - //$this->_frame->set_text(ltrim($text, "\n\r")); - - $this->_block_parent->maximize_line_height($style->height, $frame); - $this->_block_parent->add_line(); - $frame->position(); - - // Layout the new line - $this->_layout_line(); - } else if ($split < mb_strlen($frame->get_text())) { - // split the line if required - $frame->split_text($split); - - $t = $frame->get_text(); - - // Remove any trailing newlines - if ($split > 1 && $t[$split - 1] === "\n" && !$frame->is_pre()) { - $frame->set_text(mb_substr($t, 0, -1)); - } - - // Do we need to trim spaces on wrapped lines? This might be desired, however, we - // can't trim the lines here or the layout will be affected if trimming the line - // leaves enough space to fit the next word in the text stream (because pdf layout - // is performed elsewhere). - /*if (!$this->_frame->get_prev_sibling() && !$this->_frame->get_next_sibling()) { - $t = $this->_frame->get_text(); - $this->_frame->set_text( trim($t) ); - }*/ - } - - if ($add_line) { - $this->_block_parent->add_line(); - $frame->position(); - } - } else { - // Remove empty space from start and end of line, but only where there isn't an inline sibling - // and the parent node isn't an inline element with siblings - // FIXME: Include non-breaking spaces? - $t = $frame->get_text(); - $parent = $frame->get_parent(); - $is_inline_frame = ($parent instanceof \Dompdf\FrameDecorator\Inline); - - if ((!$is_inline_frame && !$frame->get_next_sibling()) /* || - ( $is_inline_frame && !$parent->get_next_sibling())*/ - ) { // fails BOLD UNDERLINED becomes BOLDUNDERLINED - $t = rtrim($t); - } - - if ((!$is_inline_frame && !$frame->get_prev_sibling()) /* || - ( $is_inline_frame && !$parent->get_prev_sibling())*/ - ) { // AB C fails (the whitespace is removed) - $t = ltrim($t); - } - - $frame->set_text($t); - } - - // Set our new width - $width = $frame->recalculate_width(); - } - - /** - * @param BlockFrameDecorator|null $block - */ - function reflow(BlockFrameDecorator $block = null) - { - $frame = $this->_frame; - $page = $frame->get_root(); - $page->check_forced_page_break($this->_frame); - - if ($page->is_full()) { - return; - } - - $this->_block_parent = /*isset($block) ? $block : */ - $frame->find_block_parent(); - - // Left trim the text if this is the first text on the line and we're - // collapsing white space -// if ( $this->_block_parent->get_current_line()->w == 0 && -// ($frame->get_style()->white_space !== "pre" || -// $frame->get_style()->white_space !== "pre-wrap") ) { -// $frame->set_text( ltrim( $frame->get_text() ) ); -// } - - $frame->position(); - - $this->_layout_line(); - - if ($block) { - $block->add_frame_to_line($frame); - } - } - - //........................................................................ - - // Returns an array(0 => min, 1 => max, "min" => min, "max" => max) of the - // minimum and maximum widths of this frame - function get_min_max_width() - { - /*if ( !is_null($this->_min_max_cache) ) - return $this->_min_max_cache;*/ - $frame = $this->_frame; - $style = $frame->get_style(); - $this->_block_parent = $frame->find_block_parent(); - $line_width = $frame->get_containing_block("w"); - - $str = $text = $frame->get_text(); - $size = $style->font_size; - $font = $style->font_family; - - $word_spacing = (float)$style->length_in_pt($style->word_spacing); - $char_spacing = (float)$style->length_in_pt($style->letter_spacing); - - switch ($style->white_space) { - default: - case "normal": - $str = preg_replace(self::$_whitespace_pattern, " ", $str); - case "pre-wrap": - case "pre-line": - - // Find the longest word (i.e. minimum length) - - // This technique (using arrays & an anonymous function) is actually - // faster than doing a single-pass character by character scan. Heh, - // yes I took the time to bench it ;) - $words = array_flip(preg_split("/[\s-]+/u", $str, -1, PREG_SPLIT_DELIM_CAPTURE)); - $root = $this; - array_walk($words, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { - $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); - }); - - arsort($words); - $min = reset($words); - break; - - case "pre": - $lines = array_flip(preg_split("/\n/u", $str)); - $root = $this; - array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { - $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); - }); - - arsort($lines); - $min = reset($lines); - break; - - case "nowrap": - $min = $this->getFontMetrics()->getTextWidth($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing); - break; - } - - switch ($style->white_space) { - default: - case "normal": - case "nowrap": - $str = preg_replace(self::$_whitespace_pattern, " ", $text); - break; - - case "pre-line": - //XXX: Is this correct? - $str = preg_replace("/[ \t]+/u", " ", $text); - - case "pre-wrap": - // Find the longest word (i.e. minimum length) - $lines = array_flip(preg_split("/\n/", $text)); - $root = $this; - array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { - $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); - }); - arsort($lines); - reset($lines); - $str = key($lines); - break; - } - - $max = $this->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); - - $delta = (float)$style->length_in_pt(array($style->margin_left, - $style->border_left_width, - $style->padding_left, - $style->padding_right, - $style->border_right_width, - $style->margin_right), $line_width); - $min += $delta; - $min_word = $min; - $max += $delta; - - if ($style->word_wrap === 'break-word') { - // If it is allowed to break words, the min width is the widest character. - // But for performance reasons, we only check the first character. - $char = mb_substr($str, 0, 1); - $min_char = $this->getFontMetrics()->getTextWidth($char, $font, $size, $word_spacing, $char_spacing); - $min = $delta + $min_char; - } - - return $this->_min_max_cache = array($min, $max, $min_word, "min" => $min, "max" => $max, 'min_word' => $min_word); - } - - /** - * @param FontMetrics $fontMetrics - * @return $this - */ - public function setFontMetrics(FontMetrics $fontMetrics) - { - $this->fontMetrics = $fontMetrics; - return $this; - } - - /** - * @return FontMetrics - */ - public function getFontMetrics() - { - return $this->fontMetrics; - } - - /** - * Determine current frame width based on contents - * - * @return float - */ - public function calculate_auto_width() - { - return $this->_frame->recalculate_width(); - } -} diff --git a/library/vendor/dompdf/src/Image/Cache.php b/library/vendor/dompdf/src/Image/Cache.php deleted file mode 100644 index e1139b11d..000000000 --- a/library/vendor/dompdf/src/Image/Cache.php +++ /dev/null @@ -1,186 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\Image; - -use Dompdf\Dompdf; -use Dompdf\Helpers; -use Dompdf\Exception\ImageException; - -/** - * Static class that resolves image urls and downloads and caches - * remote images if required. - * - * @package dompdf - */ -class Cache -{ - /** - * Array of downloaded images. Cached so that identical images are - * not needlessly downloaded. - * - * @var array - */ - protected static $_cache = array(); - - /** - * The url to the "broken image" used when images can't be loaded - * - * @var string - */ - public static $broken_image = "data:image/svg+xml;charset=utf8,%3C?xml version='1.0'?%3E%3Csvg width='64' height='64' xmlns='http://www.w3.org/2000/svg'%3E%3Cg%3E%3Crect stroke='%23666666' id='svg_1' height='60.499994' width='60.166667' y='1.666669' x='1.999998' stroke-width='1.5' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_3' y2='59.333253' x2='59.749916' y1='4.333415' x1='4.250079' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_4' y2='59.999665' x2='4.062838' y1='3.750342' x1='60.062164' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3C/g%3E%3C/svg%3E"; - - public static $error_message = "Image not found or type unknown"; - - /** - * Current dompdf instance - * - * @var Dompdf - */ - protected static $_dompdf; - - /** - * Resolve and fetch an image for use. - * - * @param string $url The url of the image - * @param string $protocol Default protocol if none specified in $url - * @param string $host Default host if none specified in $url - * @param string $base_path Default path if none specified in $url - * @param Dompdf $dompdf The Dompdf instance - * - * @throws ImageException - * @return array An array with two elements: The local path to the image and the image extension - */ - static function resolve_url($url, $protocol, $host, $base_path, Dompdf $dompdf) - { - self::$_dompdf = $dompdf; - - $protocol = mb_strtolower($protocol); - $parsed_url = Helpers::explode_url($url); - $message = null; - - $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != ""); - - $data_uri = strpos($parsed_url['protocol'], "data:") === 0; - $full_url = null; - $enable_remote = $dompdf->getOptions()->getIsRemoteEnabled(); - - try { - - // Remote not allowed and is not DataURI - if (!$enable_remote && $remote && !$data_uri) { - throw new ImageException("Remote file access is disabled.", E_WARNING); - } // Remote allowed or DataURI - else { - if ($enable_remote && $remote || $data_uri) { - // Download remote files to a temporary directory - $full_url = Helpers::build_url($protocol, $host, $base_path, $url); - - // From cache - if (isset(self::$_cache[$full_url])) { - $resolved_url = self::$_cache[$full_url]; - } // From remote - else { - $tmp_dir = $dompdf->getOptions()->getTempDir(); - $resolved_url = @tempnam($tmp_dir, "ca_dompdf_img_"); - $image = ""; - - if ($data_uri) { - if ($parsed_data_uri = Helpers::parse_data_uri($url)) { - $image = $parsed_data_uri['data']; - } - } else { - list($image, $http_response_header) = Helpers::getFileContent($full_url, $dompdf->getHttpContext()); - } - - // Image not found or invalid - if (strlen($image) == 0) { - $msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found"); - throw new ImageException($msg, E_WARNING); - } // Image found, put in cache and process - else { - //e.g. fetch.php?media=url.jpg&cache=1 - //- Image file name might be one of the dynamic parts of the url, don't strip off! - //- a remote url does not need to have a file extension at all - //- local cached file does not have a matching file extension - //Therefore get image type from the content - file_put_contents($resolved_url, $image); - } - } - } // Not remote, local image - else { - $resolved_url = Helpers::build_url($protocol, $host, $base_path, $url); - } - } - - // Check if the local file is readable - if (!is_readable($resolved_url) || !filesize($resolved_url)) { - throw new ImageException("Image not readable or empty", E_WARNING); - } // Check is the file is an image - else { - list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $dompdf->getHttpContext()); - - // Known image type - if ($width && $height && in_array($type, array("gif", "png", "jpeg", "bmp", "svg"))) { - //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup. - //Only execute on successful caching of remote image. - if ($enable_remote && $remote || $data_uri) { - self::$_cache[$full_url] = $resolved_url; - } - } // Unknown image type - else { - throw new ImageException("Image type unknown", E_WARNING); - } - } - } catch (ImageException $e) { - $resolved_url = self::$broken_image; - $type = "png"; - $message = self::$error_message; - Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine()); - } - - return array($resolved_url, $type, $message); - } - - /** - * Unlink all cached images (i.e. temporary images either downloaded - * or converted) - */ - static function clear() - { - if (empty(self::$_cache) || self::$_dompdf->getOptions()->getDebugKeepTemp()) { - return; - } - - foreach (self::$_cache as $file) { - if (self::$_dompdf->getOptions()->getDebugPng()) { - print "[clear unlink $file]"; - } - unlink($file); - } - - self::$_cache = array(); - } - - static function detect_type($file, $context = null) - { - list(, , $type) = Helpers::dompdf_getimagesize($file, $context); - - return $type; - } - - static function is_broken($url) - { - return $url === self::$broken_image; - } -} - -if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.svg"))) { - Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.svg"); -} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Positioner/Absolute.php b/library/vendor/dompdf/src/Positioner/Absolute.php deleted file mode 100644 index 30dac509a..000000000 --- a/library/vendor/dompdf/src/Positioner/Absolute.php +++ /dev/null @@ -1,118 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -namespace Dompdf\Positioner; - -use Dompdf\FrameDecorator\AbstractFrameDecorator; - -/** - * Positions absolutely positioned frames - */ -class Absolute extends AbstractPositioner -{ - - /** - * @param AbstractFrameDecorator $frame - */ - function position(AbstractFrameDecorator $frame) - { - $style = $frame->get_style(); - - $p = $frame->find_positionned_parent(); - - list($x, $y, $w, $h) = $frame->get_containing_block(); - - $top = $style->length_in_pt($style->top, $h); - $right = $style->length_in_pt($style->right, $w); - $bottom = $style->length_in_pt($style->bottom, $h); - $left = $style->length_in_pt($style->left, $w); - - if ($p && !($left === "auto" && $right === "auto")) { - // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top) - list($x, $y, $w, $h) = $p->get_padding_box(); - } - - list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height()); - - $orig_style = $frame->get_original_style(); - $orig_width = $orig_style->width; - $orig_height = $orig_style->height; - - /**************************** - * - * Width auto: - * ____________| left=auto | left=fixed | - * right=auto | A | B | - * right=fixed | C | D | - * - * Width fixed: - * ____________| left=auto | left=fixed | - * right=auto | E | F | - * right=fixed | G | H | - *****************************/ - - if ($left === "auto") { - if ($right === "auto") { - // A or E - Keep the frame at the same position - $x = $x + $frame->find_block_parent()->get_current_line_box()->w; - } else { - if ($orig_width === "auto") { - // C - $x += $w - $width - $right; - } else { - // G - $x += $w - $width - $right; - } - } - } else { - if ($right === "auto") { - // B or F - $x += (float)$left; - } else { - if ($orig_width === "auto") { - // D - TODO change width - $x += (float)$left; - } else { - // H - Everything is fixed: left + width win - $x += (float)$left; - } - } - } - - // The same vertically - if ($top === "auto") { - if ($bottom === "auto") { - // A or E - Keep the frame at the same position - $y = $frame->find_block_parent()->get_current_line_box()->y; - } else { - if ($orig_height === "auto") { - // C - $y += (float)$h - $height - (float)$bottom; - } else { - // G - $y += (float)$h - $height - (float)$bottom; - } - } - } else { - if ($bottom === "auto") { - // B or F - $y += (float)$top; - } else { - if ($orig_height === "auto") { - // D - TODO change height - $y += (float)$top; - } else { - // H - Everything is fixed: top + height win - $y += (float)$top; - } - } - } - - $frame->set_position($x, $y); - } -} diff --git a/library/vendor/dompdf/src/Positioner/Fixed.php b/library/vendor/dompdf/src/Positioner/Fixed.php deleted file mode 100644 index 556254b85..000000000 --- a/library/vendor/dompdf/src/Positioner/Fixed.php +++ /dev/null @@ -1,89 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -namespace Dompdf\Positioner; - -use Dompdf\FrameDecorator\AbstractFrameDecorator; - -/** - * Positions fixely positioned frames - */ -class Fixed extends AbstractPositioner -{ - - /** - * @param AbstractFrameDecorator $frame - */ - function position(AbstractFrameDecorator $frame) - { - $style = $frame->get_original_style(); - $root = $frame->get_root(); - $initialcb = $root->get_containing_block(); - $initialcb_style = $root->get_style(); - - $p = $frame->find_block_parent(); - if ($p) { - $p->add_line(); - } - - // Compute the margins of the @page style - $margin_top = (float)$initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]); - $margin_right = (float)$initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]); - $margin_bottom = (float)$initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]); - $margin_left = (float)$initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]); - - // The needed computed style of the element - $height = (float)$style->length_in_pt($style->height, $initialcb["h"]); - $width = (float)$style->length_in_pt($style->width, $initialcb["w"]); - - $top = $style->length_in_pt($style->top, $initialcb["h"]); - $right = $style->length_in_pt($style->right, $initialcb["w"]); - $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]); - $left = $style->length_in_pt($style->left, $initialcb["w"]); - - $y = $margin_top; - if (isset($top)) { - $y = (float)$top + $margin_top; - if ($top === "auto") { - $y = $margin_top; - if (isset($bottom) && $bottom !== "auto") { - $y = $initialcb["h"] - $bottom - $margin_bottom; - if ($frame->is_auto_height()) { - $y -= $height; - } else { - $y -= $frame->get_margin_height(); - } - } - } - } - - $x = $margin_left; - if (isset($left)) { - $x = (float)$left + $margin_left; - if ($left === "auto") { - $x = $margin_left; - if (isset($right) && $right !== "auto") { - $x = $initialcb["w"] - $right - $margin_right; - if ($frame->is_auto_width()) { - $x -= $width; - } else { - $x -= $frame->get_margin_width(); - } - } - } - } - - $frame->set_position($x, $y); - - $children = $frame->get_children(); - foreach ($children as $child) { - $child->set_position($x, $y); - } - } -} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Positioner/Inline.php b/library/vendor/dompdf/src/Positioner/Inline.php deleted file mode 100644 index bcea2ba04..000000000 --- a/library/vendor/dompdf/src/Positioner/Inline.php +++ /dev/null @@ -1,77 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -namespace Dompdf\Positioner; - -use Dompdf\FrameDecorator\AbstractFrameDecorator; -use Dompdf\FrameDecorator\Inline as InlineFrameDecorator; -use Dompdf\Exception; - -/** - * Positions inline frames - * - * @package dompdf - */ -class Inline extends AbstractPositioner -{ - - /** - * @param AbstractFrameDecorator $frame - * @throws Exception - */ - function position(AbstractFrameDecorator $frame) - { - /** - * Find our nearest block level parent and access its lines property. - * @var BlockFrameDecorator - */ - $p = $frame->find_block_parent(); - - // Debugging code: - - // Helpers::pre_r("\nPositioning:"); - // Helpers::pre_r("Me: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")"); - // Helpers::pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")"); - - // End debugging - - if (!$p) { - throw new Exception("No block-level parent found. Not good."); - } - - $f = $frame; - - $cb = $f->get_containing_block(); - $line = $p->get_current_line_box(); - - // Skip the page break if in a fixed position element - $is_fixed = false; - while ($f = $f->get_parent()) { - if ($f->get_style()->position === "fixed") { - $is_fixed = true; - break; - } - } - - $f = $frame; - - if (!$is_fixed && $f->get_parent() && - $f->get_parent() instanceof InlineFrameDecorator && - $f->is_text_node() - ) { - $min_max = $f->get_reflower()->get_min_max_width(); - - // If the frame doesn't fit in the current line, a line break occurs - if ($min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right)) { - $p->add_line(); - } - } - - $f->set_position($cb["x"] + $line->w, $line->y); - } -} diff --git a/library/vendor/dompdf/src/Positioner/ListBullet.php b/library/vendor/dompdf/src/Positioner/ListBullet.php deleted file mode 100644 index 36691f232..000000000 --- a/library/vendor/dompdf/src/Positioner/ListBullet.php +++ /dev/null @@ -1,76 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -namespace Dompdf\Positioner; - -use Dompdf\FrameDecorator\AbstractFrameDecorator; - -/** - * Positions list bullets - * - * @package dompdf - */ -class ListBullet extends AbstractPositioner -{ - - /** - * @param AbstractFrameDecorator $frame - */ - function position(AbstractFrameDecorator $frame) - { - - // Bullets & friends are positioned an absolute distance to the left of - // the content edge of their parent element - $cb = $frame->get_containing_block(); - - // Note: this differs from most frames in that we must position - // ourselves after determining our width - $x = $cb["x"] - $frame->get_width(); - - $p = $frame->find_block_parent(); - - $y = $p->get_current_line_box()->y; - - // This is a bit of a hack... - $n = $frame->get_next_sibling(); - if ($n) { - $style = $n->get_style(); - $line_height = $style->length_in_pt($style->line_height, $style->font_size); - $offset = (float)$style->length_in_pt($line_height, $n->get_containing_block("h")) - $frame->get_height(); - $y += $offset / 2; - } - - // Now the position is the left top of the block which should be marked with the bullet. - // We tried to find out the y of the start of the first text character within the block. - // But the top margin/padding does not fit, neither from this nor from the next sibling - // The "bit of a hack" above does not work also. - - // Instead let's position the bullet vertically centered to the block which should be marked. - // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent() - // the get_containing_block is paper width and the entire list as height. - - // if ($p) { - // //$cb = $n->get_containing_block(); - // $cb = $p->get_containing_block(); - // $y += $cb["h"]/2; - // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':'; - // } - - // Todo: - // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing - - /*$style = $p->get_style(); - $font_size = $style->font_size; - $line_height = (float)$style->length_in_pt($style->line_height, $font_size); - $y += ($line_height - $font_size) / 2; */ - - //Position is x-end y-top of character position of the bullet. - $frame->set_position($x, $y); - } -} diff --git a/library/vendor/dompdf/src/Renderer/AbstractRenderer.php b/library/vendor/dompdf/src/Renderer/AbstractRenderer.php deleted file mode 100644 index 502e78a02..000000000 --- a/library/vendor/dompdf/src/Renderer/AbstractRenderer.php +++ /dev/null @@ -1,923 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\Renderer; - -use Dompdf\Adapter\CPDF; -use Dompdf\Css\Color; -use Dompdf\Css\Style; -use Dompdf\Dompdf; -use Dompdf\Helpers; -use Dompdf\Frame; -use Dompdf\Image\Cache; - -/** - * Base renderer class - * - * @package dompdf - */ -abstract class AbstractRenderer -{ - - /** - * Rendering backend - * - * @var \Dompdf\Canvas - */ - protected $_canvas; - - /** - * Current dompdf instance - * - * @var Dompdf - */ - protected $_dompdf; - - /** - * Class constructor - * - * @param Dompdf $dompdf The current dompdf instance - */ - function __construct(Dompdf $dompdf) - { - $this->_dompdf = $dompdf; - $this->_canvas = $dompdf->getCanvas(); - } - - /** - * Render a frame. - * - * Specialized in child classes - * - * @param Frame $frame The frame to render - */ - abstract function render(Frame $frame); - - /** - * Render a background image over a rectangular area - * - * @param string $url The background image to load - * @param float $x The left edge of the rectangular area - * @param float $y The top edge of the rectangular area - * @param float $width The width of the rectangular area - * @param float $height The height of the rectangular area - * @param Style $style The associated Style object - * - * @throws \Exception - */ - protected function _background_image($url, $x, $y, $width, $height, $style) - { - if (!function_exists("imagecreatetruecolor")) { - throw new \Exception("The PHP GD extension is required, but is not installed."); - } - - $sheet = $style->get_stylesheet(); - - // Skip degenerate cases - if ($width == 0 || $height == 0) { - return; - } - - $box_width = $width; - $box_height = $height; - - //debugpng - if ($this->_dompdf->getOptions()->getDebugPng()) { - print '[_background_image ' . $url . ']'; - } - - list($img, $type, /*$msg*/) = Cache::resolve_url( - $url, - $sheet->get_protocol(), - $sheet->get_host(), - $sheet->get_base_path(), - $this->_dompdf - ); - - // Bail if the image is no good - if (Cache::is_broken($img)) { - return; - } - - //Try to optimize away reading and composing of same background multiple times - //Postponing read with imagecreatefrom ...() - //final composition parameters and name not known yet - //Therefore read dimension directly from file, instead of creating gd object first. - //$img_w = imagesx($src); $img_h = imagesy($src); - - list($img_w, $img_h) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext()); - if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) { - return; - } - - $repeat = $style->background_repeat; - $dpi = $this->_dompdf->getOptions()->getDpi(); - - //Increase background resolution and dependent box size according to image resolution to be placed in - //Then image can be copied in without resize - $bg_width = round((float)($width * $dpi) / 72); - $bg_height = round((float)($height * $dpi) / 72); - - //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel - - list($bg_x, $bg_y) = $style->background_position; - - if (Helpers::is_percent($bg_x)) { - // The point $bg_x % from the left edge of the image is placed - // $bg_x % from the left edge of the background rectangle - $p = ((float)$bg_x) / 100.0; - $x1 = $p * $img_w; - $x2 = $p * $bg_width; - - $bg_x = $x2 - $x1; - } else { - $bg_x = (float)($style->length_in_pt($bg_x) * $dpi) / 72; - } - - $bg_x = round($bg_x + (float)$style->length_in_pt($style->border_left_width) * $dpi / 72); - - if (Helpers::is_percent($bg_y)) { - // The point $bg_y % from the left edge of the image is placed - // $bg_y % from the left edge of the background rectangle - $p = ((float)$bg_y) / 100.0; - $y1 = $p * $img_h; - $y2 = $p * $bg_height; - - $bg_y = $y2 - $y1; - } else { - $bg_y = (float)($style->length_in_pt($bg_y) * $dpi) / 72; - } - - $bg_y = round($bg_y + (float)$style->length_in_pt($style->border_top_width) * $dpi / 72); - - //clip background to the image area on partial repeat. Nothing to do if img off area - //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area - //On no repeat with positive offset: move size/start to have offset==0 - //Handle x/y Dimensions separately - - if ($repeat !== "repeat" && $repeat !== "repeat-x") { - //No repeat x - if ($bg_x < 0) { - $bg_width = $img_w + $bg_x; - } else { - $x += ($bg_x * 72) / $dpi; - $bg_width = $bg_width - $bg_x; - if ($bg_width > $img_w) { - $bg_width = $img_w; - } - $bg_x = 0; - } - - if ($bg_width <= 0) { - return; - } - - $width = (float)($bg_width * 72) / $dpi; - } else { - //repeat x - if ($bg_x < 0) { - $bg_x = -((-$bg_x) % $img_w); - } else { - $bg_x = $bg_x % $img_w; - if ($bg_x > 0) { - $bg_x -= $img_w; - } - } - } - - if ($repeat !== "repeat" && $repeat !== "repeat-y") { - //no repeat y - if ($bg_y < 0) { - $bg_height = $img_h + $bg_y; - } else { - $y += ($bg_y * 72) / $dpi; - $bg_height = $bg_height - $bg_y; - if ($bg_height > $img_h) { - $bg_height = $img_h; - } - $bg_y = 0; - } - if ($bg_height <= 0) { - return; - } - $height = (float)($bg_height * 72) / $dpi; - } else { - //repeat y - if ($bg_y < 0) { - $bg_y = -((-$bg_y) % $img_h); - } else { - $bg_y = $bg_y % $img_h; - if ($bg_y > 0) { - $bg_y -= $img_h; - } - } - } - - //Optimization, if repeat has no effect - if ($repeat === "repeat" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) { - $repeat = "repeat-x"; - } - - if ($repeat === "repeat" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) { - $repeat = "repeat-y"; - } - - if (($repeat === "repeat-x" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) || - ($repeat === "repeat-y" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) - ) { - $repeat = "no-repeat"; - } - - //Use filename as indicator only - //different names for different variants to have different copies in the pdf - //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color) - //Note: Here, bg_* are the start values, not end values after going through the tile loops! - - $filedummy = $img; - - $is_png = false; - $filedummy .= '_' . $bg_width . '_' . $bg_height . '_' . $bg_x . '_' . $bg_y . '_' . $repeat; - - //Optimization to avoid multiple times rendering the same image. - //If check functions are existing and identical image already cached, - //then skip creation of duplicate, because it is not needed by addImagePng - if ($this->_canvas instanceof CPDF && $this->_canvas->get_cpdf()->image_iscached($filedummy)) { - $bg = null; - } else { - // Create a new image to fit over the background rectangle - $bg = imagecreatetruecolor($bg_width, $bg_height); - - switch (strtolower($type)) { - case "png": - $is_png = true; - imagesavealpha($bg, true); - imagealphablending($bg, false); - $src = imagecreatefrompng($img); - break; - - case "jpeg": - $src = imagecreatefromjpeg($img); - break; - - case "gif": - $src = imagecreatefromgif($img); - break; - - case "bmp": - $src = Helpers::imagecreatefrombmp($img); - break; - - default: - return; // Unsupported image type - } - - if ($src == null) { - return; - } - - //Background color if box is not relevant here - //Non transparent image: box clipped to real size. Background non relevant. - //Transparent image: The image controls the transparency and lets shine through whatever background. - //However on transparent image preset the composed image with the transparency color, - //to keep the transparency when copying over the non transparent parts of the tiles. - $ti = imagecolortransparent($src); - - if ($ti >= 0) { - $tc = imagecolorsforindex($src, $ti); - $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']); - imagefill($bg, 0, 0, $ti); - imagecolortransparent($bg, $ti); - } - - //This has only an effect for the non repeatable dimension. - //compute start of src and dest coordinates of the single copy - if ($bg_x < 0) { - $dst_x = 0; - $src_x = -$bg_x; - } else { - $src_x = 0; - $dst_x = $bg_x; - } - - if ($bg_y < 0) { - $dst_y = 0; - $src_y = -$bg_y; - } else { - $src_y = 0; - $dst_y = $bg_y; - } - - //For historical reasons exchange meanings of variables: - //start_* will be the start values, while bg_* will be the temporary start values in the loops - $start_x = $bg_x; - $start_y = $bg_y; - - // Copy regions from the source image to the background - if ($repeat === "no-repeat") { - // Simply place the image on the background - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h); - - } else if ($repeat === "repeat-x") { - for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { - if ($bg_x < 0) { - $dst_x = 0; - $src_x = -$bg_x; - $w = $img_w + $bg_x; - } else { - $dst_x = $bg_x; - $src_x = 0; - $w = $img_w; - } - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h); - } - } else if ($repeat === "repeat-y") { - - for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { - if ($bg_y < 0) { - $dst_y = 0; - $src_y = -$bg_y; - $h = $img_h + $bg_y; - } else { - $dst_y = $bg_y; - $src_y = 0; - $h = $img_h; - } - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h); - } - } else if ($repeat === "repeat") { - for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { - for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { - if ($bg_x < 0) { - $dst_x = 0; - $src_x = -$bg_x; - $w = $img_w + $bg_x; - } else { - $dst_x = $bg_x; - $src_x = 0; - $w = $img_w; - } - - if ($bg_y < 0) { - $dst_y = 0; - $src_y = -$bg_y; - $h = $img_h + $bg_y; - } else { - $dst_y = $bg_y; - $src_y = 0; - $h = $img_h; - } - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h); - } - } - } else { - print 'Unknown repeat!'; - } - - imagedestroy($src); - - } /* End optimize away creation of duplicates */ - - $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height); - - //img: image url string - //img_w, img_h: original image size in px - //width, height: box size in pt - //bg_width, bg_height: box size in px - //x, y: left/top edge of box on page in pt - //start_x, start_y: placement of image relative to pattern - //$repeat: repeat mode - //$bg: GD object of result image - //$src: GD object of original image - //When using cpdf and optimization to direct png creation from gd object is available, - //don't create temp file, but place gd object directly into the pdf - if (!$is_png && $this->_canvas instanceof CPDF) { - // Note: CPDF_Adapter image converts y position - $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg); - } else { - $tmp_dir = $this->_dompdf->getOptions()->getTempDir(); - $tmp_name = @tempnam($tmp_dir, "bg_dompdf_img_"); - @unlink($tmp_name); - $tmp_file = "$tmp_name.png"; - - //debugpng - if ($this->_dompdf->getOptions()->getDebugPng()) { - print '[_background_image ' . $tmp_file . ']'; - } - - imagepng($bg, $tmp_file); - $this->_canvas->image($tmp_file, $x, $y, $width, $height); - imagedestroy($bg); - - //debugpng - if ($this->_dompdf->getOptions()->getDebugPng()) { - print '[_background_image unlink ' . $tmp_file . ']'; - } - - if (!$this->_dompdf->getOptions()->getDebugKeepTemp()) { - unlink($tmp_file); - } - } - - $this->_canvas->clipping_end(); - } - - /** - * @param $style - * @param $width - * @return array - */ - protected function _get_dash_pattern($style, $width) - { - $pattern = array(); - - switch ($style) { - default: - /*case "solid": - case "double": - case "groove": - case "inset": - case "outset": - case "ridge":*/ - case "none": - break; - - case "dotted": - if ($width <= 1) { - $pattern = array($width, $width * 2); - } else { - $pattern = array($width); - } - break; - - case "dashed": - $pattern = array(3 * $width); - break; - } - - return $pattern; - } - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - return; - } - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - return; - } - - // Border rendering functions - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2); - } - - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2); - } - - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't. - if ($corner_style !== "bevel" || $r1 > 0 || $r2 > 0) { - // do it the simple way - $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2); - return; - } - - list($top, $right, $bottom, $left) = $widths; - - // All this polygon business is for beveled corners... - switch ($side) { - case "top": - $points = array($x, $y, - $x + $length, $y, - $x + $length - $right, $y + $top, - $x + $left, $y + $top); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - case "bottom": - $points = array($x, $y, - $x + $length, $y, - $x + $length - $right, $y - $bottom, - $x + $left, $y - $bottom); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - case "left": - $points = array($x, $y, - $x, $y + $length, - $x + $left, $y + $length - $bottom, - $x + $left, $y + $top); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - case "right": - $points = array($x, $y, - $x, $y + $length, - $x - $right, $y + $length - $bottom, - $x - $right, $y + $top); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - default: - return; - } - } - - /** - * @param $side - * @param $ratio - * @param $top - * @param $right - * @param $bottom - * @param $left - * @param $x - * @param $y - * @param $length - * @param $r1 - * @param $r2 - */ - protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) - { - switch ($side) { - case "top": - $r1 -= $left * $ratio; - $r2 -= $right * $ratio; - $x += $left * $ratio; - $y += $top * $ratio; - $length -= $left * $ratio + $right * $ratio; - break; - - case "bottom": - $r1 -= $right * $ratio; - $r2 -= $left * $ratio; - $x += $left * $ratio; - $y -= $bottom * $ratio; - $length -= $left * $ratio + $right * $ratio; - break; - - case "left": - $r1 -= $top * $ratio; - $r2 -= $bottom * $ratio; - $x += $left * $ratio; - $y += $top * $ratio; - $length -= $top * $ratio + $bottom * $ratio; - break; - - case "right": - $r1 -= $bottom * $ratio; - $r2 -= $top * $ratio; - $x -= $right * $ratio; - $y += $top * $ratio; - $length -= $top * $ratio + $bottom * $ratio; - break; - - default: - return; - } - } - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - list($top, $right, $bottom, $left) = $widths; - - $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3); - - // draw the outer border - $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); - - $this->_apply_ratio($side, 2 / 3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); - - $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); - } - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - list($top, $right, $bottom, $left) = $widths; - - $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); - - $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); - - $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - } - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - list($top, $right, $bottom, $left) = $widths; - - $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); - - $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); - - $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - } - - /** - * @param $c - * @return mixed - */ - protected function _tint($c) - { - if (!is_numeric($c)) { - return $c; - } - - return min(1, $c + 0.16); - } - - /** - * @param $c - * @return mixed - */ - protected function _shade($c) - { - if (!is_numeric($c)) { - return $c; - } - - return max(0, $c - 0.33); - } - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - switch ($side) { - case "top": - case "left": - $shade = array_map(array($this, "_shade"), $color); - $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); - break; - - case "bottom": - case "right": - $tint = array_map(array($this, "_tint"), $color); - $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); - break; - - default: - return; - } - } - - /** - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param int $r1 - * @param int $r2 - */ - protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) - { - switch ($side) { - case "top": - case "left": - $tint = array_map(array($this, "_tint"), $color); - $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); - break; - - case "bottom": - case "right": - $shade = array_map(array($this, "_shade"), $color); - $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); - break; - - default: - return; - } - } - - /** - * Draws a solid, dotted, or dashed line, observing the border radius - * - * @param $x - * @param $y - * @param $length - * @param $color - * @param $widths - * @param $side - * @param string $corner_style - * @param $pattern_name - * @param int $r1 - * @param int $r2 - * - * @var $top - */ - protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) - { - /** used by $$side */ - list($top, $right, $bottom, $left) = $widths; - $width = $$side; - - $pattern = $this->_get_dash_pattern($pattern_name, $width); - - $half_width = $width / 2; - $r1 -= $half_width; - $r2 -= $half_width; - $adjust = $r1 / 80; - $length -= $width; - - switch ($side) { - case "top": - $x += $half_width; - $y += $half_width; - - if ($r1 > 0) { - $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90 - $adjust, 135 + $adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); - - if ($r2 > 0) { - $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45 - $adjust, 90 + $adjust, $color, $width, $pattern); - } - break; - - case "bottom": - $x += $half_width; - $y -= $half_width; - - if ($r1 > 0) { - $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225 - $adjust, 270 + $adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); - - if ($r2 > 0) { - $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270 - $adjust, 315 + $adjust, $color, $width, $pattern); - } - break; - - case "left": - $y += $half_width; - $x += $half_width; - - if ($r1 > 0) { - $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135 - $adjust, 180 + $adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); - - if ($r2 > 0) { - $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180 - $adjust, 225 + $adjust, $color, $width, $pattern); - } - break; - - case "right": - $y += $half_width; - $x -= $half_width; - - if ($r1 > 0) { - $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0 - $adjust, 45 + $adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); - - if ($r2 > 0) { - $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315 - $adjust, 360 + $adjust, $color, $width, $pattern); - } - break; - } - } - - /** - * @param $opacity - */ - protected function _set_opacity($opacity) - { - if (is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0) { - $this->_canvas->set_opacity($opacity); - } - } - - /** - * @param $box - * @param string $color - * @param array $style - */ - protected function _debug_layout($box, $color = "red", $style = array()) - { - $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], Color::parse($color), 0.1, $style); - } -} diff --git a/library/vendor/dompdf/src/Renderer/Block.php b/library/vendor/dompdf/src/Renderer/Block.php deleted file mode 100644 index ee0705d8f..000000000 --- a/library/vendor/dompdf/src/Renderer/Block.php +++ /dev/null @@ -1,262 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\Renderer; - -use Dompdf\Frame; -use Dompdf\FrameDecorator\AbstractFrameDecorator; -use Dompdf\Helpers; - -/** - * Renders block frames - * - * @package dompdf - */ -class Block extends AbstractRenderer -{ - - /** - * @param Frame $frame - */ - function render(Frame $frame) - { - $style = $frame->get_style(); - $node = $frame->get_node(); - - list($x, $y, $w, $h) = $frame->get_border_box(); - - $this->_set_opacity($frame->get_opacity($style->opacity)); - - if ($node->nodeName === "body") { - $h = $frame->get_containing_block("h") - (float)$style->length_in_pt(array( - $style->margin_top, - $style->border_top_width, - $style->border_bottom_width, - $style->margin_bottom), - (float)$style->length_in_pt($style->width)); - } - - // Handle anchors & links - if ($node->nodeName === "a" && $href = $node->getAttribute("href")) { - $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); - $this->_canvas->add_link($href, $x, $y, (float)$w, (float)$h); - } - - // Draw our background, border and content - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - if ($tl + $tr + $br + $bl > 0) { - $this->_canvas->clipping_roundrectangle($x, $y, (float)$w, (float)$h, $tl, $tr, $br, $bl); - } - - if (($bg = $style->background_color) !== "transparent") { - $this->_canvas->filled_rectangle($x, $y, (float)$w, (float)$h, $bg); - } - - if (($url = $style->background_image) && $url !== "none") { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - if ($tl + $tr + $br + $bl > 0) { - $this->_canvas->clipping_end(); - } - - $border_box = array($x, $y, $w, $h); - $this->_render_border($frame, $border_box); - $this->_render_outline($frame, $border_box); - - if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutBlocks()) { - $this->_debug_layout($frame->get_border_box(), "red"); - if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) { - $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); - } - } - - if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines() && $frame->get_decorator()) { - foreach ($frame->get_decorator()->get_line_boxes() as $line) { - $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); - } - } - - $id = $frame->get_node()->getAttribute("id"); - if (strlen($id) > 0) { - $this->_canvas->add_named_dest($id); - } - } - - /** - * @param AbstractFrameDecorator $frame - * @param null $border_box - * @param string $corner_style - */ - protected function _render_border(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel") - { - $style = $frame->get_style(); - $bp = $style->get_border_properties(); - - if (empty($border_box)) { - $border_box = $frame->get_border_box(); - } - - // find the radius - $radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h - - // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle - if ( - in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) && - $bp["top"] == $bp["right"] && - $bp["right"] == $bp["bottom"] && - $bp["bottom"] == $bp["left"] && - array_sum($radius) == 0 - ) { - $props = $bp["top"]; - if ($props["color"] === "transparent" || $props["width"] <= 0) { - return; - } - - list($x, $y, $w, $h) = $border_box; - $width = (float)$style->length_in_pt($props["width"]); - $pattern = $this->_get_dash_pattern($props["style"], $width); - $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, (float)$w - $width, (float)$h - $width, $props["color"], $width, $pattern); - return; - } - - // Do it the long way - $widths = array( - (float)$style->length_in_pt($bp["top"]["width"]), - (float)$style->length_in_pt($bp["right"]["width"]), - (float)$style->length_in_pt($bp["bottom"]["width"]), - (float)$style->length_in_pt($bp["left"]["width"]) - ); - - foreach ($bp as $side => $props) { - list($x, $y, $w, $h) = $border_box; - $length = 0; - $r1 = 0; - $r2 = 0; - - if (!$props["style"] || - $props["style"] === "none" || - $props["width"] <= 0 || - $props["color"] == "transparent" - ) { - continue; - } - - switch ($side) { - case "top": - $length = (float)$w; - $r1 = $radius["top-left"]; - $r2 = $radius["top-right"]; - break; - - case "bottom": - $length = (float)$w; - $y += (float)$h; - $r1 = $radius["bottom-left"]; - $r2 = $radius["bottom-right"]; - break; - - case "left": - $length = (float)$h; - $r1 = $radius["top-left"]; - $r2 = $radius["bottom-left"]; - break; - - case "right": - $length = (float)$h; - $x += (float)$w; - $r1 = $radius["top-right"]; - $r2 = $radius["bottom-right"]; - break; - default: - break; - } - $method = "_border_" . $props["style"]; - - // draw rounded corners - $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2); - } - } - - /** - * @param AbstractFrameDecorator $frame - * @param null $border_box - * @param string $corner_style - */ - protected function _render_outline(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel") - { - $style = $frame->get_style(); - - $props = array( - "width" => $style->outline_width, - "style" => $style->outline_style, - "color" => $style->outline_color, - ); - - if (!$props["style"] || $props["style"] === "none" || $props["width"] <= 0) { - return; - } - - if (empty($border_box)) { - $border_box = $frame->get_border_box(); - } - - $offset = (float)$style->length_in_pt($props["width"]); - $pattern = $this->_get_dash_pattern($props["style"], $offset); - - // If the outline style is "solid" we'd better draw a rectangle - if (in_array($props["style"], array("solid", "dashed", "dotted"))) { - $border_box[0] -= $offset / 2; - $border_box[1] -= $offset / 2; - $border_box[2] += $offset; - $border_box[3] += $offset; - - list($x, $y, $w, $h) = $border_box; - $this->_canvas->rectangle($x, $y, (float)$w, (float)$h, $props["color"], $offset, $pattern); - return; - } - - $border_box[0] -= $offset; - $border_box[1] -= $offset; - $border_box[2] += $offset * 2; - $border_box[3] += $offset * 2; - - $method = "_border_" . $props["style"]; - $widths = array_fill(0, 4, (float)$style->length_in_pt($props["width"])); - $sides = array("top", "right", "left", "bottom"); - $length = 0; - - foreach ($sides as $side) { - list($x, $y, $w, $h) = $border_box; - - switch ($side) { - case "top": - $length = (float)$w; - break; - - case "bottom": - $length = (float)$w; - $y += (float)$h; - break; - - case "left": - $length = (float)$h; - break; - - case "right": - $length = (float)$h; - $x += (float)$w; - break; - default: - break; - } - - $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style); - } - } -} diff --git a/library/vendor/dompdf/src/Renderer/Image.php b/library/vendor/dompdf/src/Renderer/Image.php deleted file mode 100644 index 87333af71..000000000 --- a/library/vendor/dompdf/src/Renderer/Image.php +++ /dev/null @@ -1,139 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\Renderer; - -use Dompdf\Frame; -use Dompdf\Image\Cache; - -/** - * Image renderer - * - * @access private - * @package dompdf - */ -class Image extends Block -{ - - /** - * @param Frame $frame - */ - function render(Frame $frame) - { - // Render background & borders - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - list($x, $y, $w, $h) = $frame->get_border_box(); - - $this->_set_opacity($frame->get_opacity($style->opacity)); - - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - $has_border_radius = $tl + $tr + $br + $bl > 0; - - if ($has_border_radius) { - $this->_canvas->clipping_roundrectangle($x, $y, (float)$w, (float)$h, $tl, $tr, $br, $bl); - } - - if (($bg = $style->background_color) !== "transparent") { - $this->_canvas->filled_rectangle($x, $y, (float)$w, (float)$h, $bg); - } - - if (($url = $style->background_image) && $url !== "none") { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - if ($has_border_radius) { - $this->_canvas->clipping_end(); - } - - $this->_render_border($frame); - $this->_render_outline($frame); - - list($x, $y) = $frame->get_padding_box(); - - $x += (float)$style->length_in_pt($style->padding_left, $cb["w"]); - $y += (float)$style->length_in_pt($style->padding_top, $cb["h"]); - - $w = (float)$style->length_in_pt($style->width, $cb["w"]); - $h = (float)$style->length_in_pt($style->height, $cb["h"]); - - if ($has_border_radius) { - list($wt, $wr, $wb, $wl) = array( - $style->border_top_width, - $style->border_right_width, - $style->border_bottom_width, - $style->border_left_width, - ); - - // we have to get the "inner" radius - if ($tl > 0) { - $tl -= ($wt + $wl) / 2; - } - if ($tr > 0) { - $tr -= ($wt + $wr) / 2; - } - if ($br > 0) { - $br -= ($wb + $wr) / 2; - } - if ($bl > 0) { - $bl -= ($wb + $wl) / 2; - } - - $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); - } - - $src = $frame->get_image_url(); - $alt = null; - - if (Cache::is_broken($src) && - $alt = $frame->get_node()->getAttribute("alt") - ) { - $font = $style->font_family; - $size = $style->font_size; - $spacing = $style->word_spacing; - $this->_canvas->text( - $x, - $y, - $alt, - $font, - $size, - $style->color, - $spacing - ); - } else { - $this->_canvas->image($src, $x, $y, $w, $h, $style->image_resolution); - } - - if ($has_border_radius) { - $this->_canvas->clipping_end(); - } - - if ($msg = $frame->get_image_msg()) { - $parts = preg_split("/\s*\n\s*/", $msg); - $height = 10; - $_y = $alt ? $y + $h - count($parts) * $height : $y; - - foreach ($parts as $i => $_part) { - $this->_canvas->text($x, $_y + $i * $height, $_part, "times", $height * 0.8, array(0.5, 0.5, 0.5)); - } - } - - if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutBlocks()) { - $this->_debug_layout($frame->get_border_box(), "blue"); - if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) { - $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5)); - } - } - - $id = $frame->get_node()->getAttribute("id"); - if (strlen($id) > 0) { - $this->_canvas->add_named_dest($id); - } - } -} diff --git a/library/vendor/dompdf/src/Renderer/Inline.php b/library/vendor/dompdf/src/Renderer/Inline.php deleted file mode 100644 index 263fd6f1a..000000000 --- a/library/vendor/dompdf/src/Renderer/Inline.php +++ /dev/null @@ -1,211 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\Renderer; - -use Dompdf\Frame; -use Dompdf\Helpers; - -/** - * Renders inline frames - * - * @access private - * @package dompdf - */ -class Inline extends AbstractRenderer -{ - - /** - * @param Frame $frame - */ - function render(Frame $frame) - { - $style = $frame->get_style(); - - if (!$frame->get_first_child()) { - return; // No children, no service - } - - // Draw the left border if applicable - $bp = $style->get_border_properties(); - $widths = array( - (float)$style->length_in_pt($bp["top"]["width"]), - (float)$style->length_in_pt($bp["right"]["width"]), - (float)$style->length_in_pt($bp["bottom"]["width"]), - (float)$style->length_in_pt($bp["left"]["width"]) - ); - - // Draw the background & border behind each child. To do this we need - // to figure out just how much space each child takes: - list($x, $y) = $frame->get_first_child()->get_position(); - $w = null; - $h = 0; - // $x += $widths[3]; - // $y += $widths[0]; - - $this->_set_opacity($frame->get_opacity($style->opacity)); - - $first_row = true; - - $DEBUGLAYOUTINLINE = $this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutInline(); - - foreach ($frame->get_children() as $child) { - list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box(); - - if (!is_null($w) && $child_x < $x + $w) { - //This branch seems to be supposed to being called on the first part - //of an inline html element, and the part after the if clause for the - //parts after a line break. - //But because $w initially mostly is 0, and gets updated only on the next - //round, this seem to be never executed and the common close always. - - // The next child is on another line. Draw the background & - // borders on this line. - - // Background: - if (($bg = $style->background_color) !== "transparent") { - $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); - } - - if (($url = $style->background_image) && $url !== "none") { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - // If this is the first row, draw the left border - if ($first_row) { - if ($bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0) { - $method = "_border_" . $bp["left"]["style"]; - $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left"); - } - $first_row = false; - } - - // Draw the top & bottom borders - if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0) { - $method = "_border_" . $bp["top"]["style"]; - $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top"); - } - - if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0) { - $method = "_border_" . $bp["bottom"]["style"]; - $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom"); - } - - // Handle anchors & links - $link_node = null; - if ($frame->get_node()->nodeName === "a") { - $link_node = $frame->get_node(); - } else if ($frame->get_parent()->get_node()->nodeName === "a") { - $link_node = $frame->get_parent()->get_node(); - } - - if ($link_node && $href = $link_node->getAttribute("href")) { - $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - - $x = $child_x; - $y = $child_y; - $w = (float)$child_w; - $h = (float)$child_h; - continue; - } - - if (is_null($w)) { - $w = (float)$child_w; - }else { - $w += (float)$child_w; - } - - $h = max($h, $child_h); - - if ($DEBUGLAYOUTINLINE) { - $this->_debug_layout($child->get_border_box(), "blue"); - if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) { - $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5)); - } - } - } - - // Handle the last child - if (($bg = $style->background_color) !== "transparent") { - $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $bg); - } - - //On continuation lines (after line break) of inline elements, the style got copied. - //But a non repeatable background image should not be repeated on the next line. - //But removing the background image above has never an effect, and removing it below - //removes it always, even on the initial line. - //Need to handle it elsewhere, e.g. on certain ...clone()... usages. - // Repeat not given: default is Style::__construct - // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ... - //different position? $this->_background_image($url, $x, $y, $w, $h, $style); - if (($url = $style->background_image) && $url !== "none") { - $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style); - } - - // Add the border widths - $w += (float)$widths[1] + (float)$widths[3]; - $h += (float)$widths[0] + (float)$widths[2]; - - // make sure the border and background start inside the left margin - $left_margin = (float)$style->length_in_pt($style->margin_left); - $x += $left_margin; - - // If this is the first row, draw the left border too - if ($first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0) { - $method = "_border_" . $bp["left"]["style"]; - $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left"); - } - - // Draw the top & bottom borders - if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0) { - $method = "_border_" . $bp["top"]["style"]; - $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top"); - } - - if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0) { - $method = "_border_" . $bp["bottom"]["style"]; - $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom"); - } - - // Helpers::var_dump(get_class($frame->get_next_sibling())); - // $last_row = get_class($frame->get_next_sibling()) !== 'Inline'; - // Draw the right border if this is the last row - if ($bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0) { - $method = "_border_" . $bp["right"]["style"]; - $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right"); - } - - $id = $frame->get_node()->getAttribute("id"); - if (strlen($id) > 0) { - $this->_canvas->add_named_dest($id); - } - - // Only two levels of links frames - $link_node = null; - if ($frame->get_node()->nodeName === "a") { - $link_node = $frame->get_node(); - - if (($name = $link_node->getAttribute("name"))) { - $this->_canvas->add_named_dest($name); - } - } - - if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") { - $link_node = $frame->get_parent()->get_node(); - } - - // Handle anchors & links - if ($link_node) { - if ($href = $link_node->getAttribute("href")) { - $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - } - } -} diff --git a/library/vendor/dompdf/src/Renderer/TableRowGroup.php b/library/vendor/dompdf/src/Renderer/TableRowGroup.php deleted file mode 100644 index 6742f9970..000000000 --- a/library/vendor/dompdf/src/Renderer/TableRowGroup.php +++ /dev/null @@ -1,50 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ -namespace Dompdf\Renderer; - -use Dompdf\Frame; - -/** - * Renders block frames - * - * @package dompdf - */ -class TableRowGroup extends Block -{ - - /** - * @param Frame $frame - */ - function render(Frame $frame) - { - $style = $frame->get_style(); - - $this->_set_opacity($frame->get_opacity($style->opacity)); - - $this->_render_border($frame); - $this->_render_outline($frame); - - if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutBlocks()) { - $this->_debug_layout($frame->get_border_box(), "red"); - if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) { - $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); - } - } - - if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines() && $frame->get_decorator()) { - foreach ($frame->get_decorator()->get_line_boxes() as $line) { - $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); - } - } - - $id = $frame->get_node()->getAttribute("id"); - if (strlen($id) > 0) { - $this->_canvas->add_named_dest($id); - } - } -} diff --git a/library/vendor/dompdf/vendor/autoload.php b/library/vendor/dompdf/vendor/autoload.php new file mode 100644 index 000000000..77629e9bf --- /dev/null +++ b/library/vendor/dompdf/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var ?string */ + private $vendorDir; + + // PSR-4 + /** + * @var array[] + * @psalm-var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array[] + * @psalm-var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var array[] + * @psalm-var array + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * @var array[] + * @psalm-var array> + */ + private $prefixesPsr0 = array(); + /** + * @var array[] + * @psalm-var array + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var string[] + * @psalm-var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var bool[] + * @psalm-var array + */ + private $missingClasses = array(); + + /** @var ?string */ + private $apcuPrefix; + + /** + * @var self[] + */ + private static $registeredLoaders = array(); + + /** + * @param ?string $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + } + + /** + * @return string[] + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array[] + * @psalm-return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return array[] + * @psalm-return array + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return array[] + * @psalm-return array + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return string[] Array of classname => path + * @psalm-return array + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param string[] $classMap Class to filename map + * @psalm-param array $classMap + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + * @private + */ +function includeFile($file) +{ + include $file; +} diff --git a/library/vendor/dompdf/vendor/composer/InstalledVersions.php b/library/vendor/dompdf/vendor/composer/InstalledVersions.php new file mode 100644 index 000000000..d50e0c9fc --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/InstalledVersions.php @@ -0,0 +1,350 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints($constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = require __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + $installed[] = self::$installed; + + return $installed; + } +} diff --git a/library/vendor/dompdf/vendor/composer/LICENSE b/library/vendor/dompdf/vendor/composer/LICENSE new file mode 100644 index 000000000..f27399a04 --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/library/vendor/dompdf/vendor/composer/autoload_classmap.php b/library/vendor/dompdf/vendor/composer/autoload_classmap.php new file mode 100644 index 000000000..b708d77ba --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/autoload_classmap.php @@ -0,0 +1,11 @@ + $vendorDir . '/composer/InstalledVersions.php', + 'Dompdf\\Cpdf' => $vendorDir . '/dompdf/dompdf/lib/Cpdf.php', +); diff --git a/library/vendor/dompdf/vendor/composer/autoload_namespaces.php b/library/vendor/dompdf/vendor/composer/autoload_namespaces.php new file mode 100644 index 000000000..b7fc0125d --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/phenx/php-svg-lib/src/Svg'), + 'Sabberworm\\CSS\\' => array($vendorDir . '/sabberworm/php-css-parser/src'), + 'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'), + 'FontLib\\' => array($vendorDir . '/phenx/php-font-lib/src/FontLib'), + 'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'), +); diff --git a/library/vendor/dompdf/vendor/composer/autoload_real.php b/library/vendor/dompdf/vendor/composer/autoload_real.php new file mode 100644 index 000000000..49d5ac331 --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/autoload_real.php @@ -0,0 +1,57 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + return $loader; + } +} diff --git a/library/vendor/dompdf/vendor/composer/autoload_static.php b/library/vendor/dompdf/vendor/composer/autoload_static.php new file mode 100644 index 000000000..2c05e6cb1 --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/autoload_static.php @@ -0,0 +1,66 @@ + + array ( + 'Svg\\' => 4, + 'Sabberworm\\CSS\\' => 15, + ), + 'M' => + array ( + 'Masterminds\\' => 12, + ), + 'F' => + array ( + 'FontLib\\' => 8, + ), + 'D' => + array ( + 'Dompdf\\' => 7, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Svg\\' => + array ( + 0 => __DIR__ . '/..' . '/phenx/php-svg-lib/src/Svg', + ), + 'Sabberworm\\CSS\\' => + array ( + 0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/src', + ), + 'Masterminds\\' => + array ( + 0 => __DIR__ . '/..' . '/masterminds/html5/src', + ), + 'FontLib\\' => + array ( + 0 => __DIR__ . '/..' . '/phenx/php-font-lib/src/FontLib', + ), + 'Dompdf\\' => + array ( + 0 => __DIR__ . '/..' . '/dompdf/dompdf/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Dompdf\\Cpdf' => __DIR__ . '/..' . '/dompdf/dompdf/lib/Cpdf.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit47dfdbccdbe2c66869f4658d0723ed1a::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/library/vendor/dompdf/vendor/composer/installed.json b/library/vendor/dompdf/vendor/composer/installed.json new file mode 100644 index 000000000..0acb559d3 --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/installed.json @@ -0,0 +1,295 @@ +{ + "packages": [ + { + "name": "dompdf/dompdf", + "version": "v2.0.1", + "version_normalized": "2.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "c5310df0e22c758c85ea5288175fc6cd777bc085" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/c5310df0e22c758c85ea5288175fc6cd777bc085", + "reference": "c5310df0e22c758c85ea5288175fc6cd777bc085", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "phenx/php-font-lib": ">=0.5.4 <1.0.0", + "phenx/php-svg-lib": ">=0.3.3 <1.0.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "time": "2022-09-22T13:43:41+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v2.0.1" + }, + "install-path": "../dompdf/dompdf" + }, + { + "name": "masterminds/html5", + "version": "2.7.6", + "version_normalized": "2.7.6.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "897eb517a343a2281f11bc5556d6548db7d93947" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947", + "reference": "897eb517a343a2281f11bc5556d6548db7d93947", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" + }, + "time": "2022-08-18T16:18:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.7.6" + }, + "install-path": "../masterminds/html5" + }, + { + "name": "phenx/php-font-lib", + "version": "0.5.4", + "version_normalized": "0.5.4.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/dd448ad1ce34c63d09baccd05415e361300c35b4", + "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4", + "shasum": "" + }, + "require": { + "ext-mbstring": "*" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3 || ^4 || ^5" + }, + "time": "2021-12-17T19:44:54+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/PhenX/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/0.5.4" + }, + "install-path": "../phenx/php-font-lib" + }, + { + "name": "phenx/php-svg-lib", + "version": "0.5.0", + "version_normalized": "0.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "76876c6cf3080bcb6f249d7d59705108166a6685" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/76876c6cf3080bcb6f249d7d59705108166a6685", + "reference": "76876c6cf3080bcb6f249d7d59705108166a6685", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "time": "2022-09-06T12:16:56+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/PhenX/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.0" + }, + "install-path": "../phenx/php-svg-lib" + }, + { + "name": "sabberworm/php-css-parser", + "version": "8.4.0", + "version_normalized": "8.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", + "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30", + "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=5.6.20" + }, + "require-dev": { + "codacy/coverage": "^1.4", + "phpunit/phpunit": "^4.8.36" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "time": "2021-12-11T13:40:54+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", + "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0" + }, + "install-path": "../sabberworm/php-css-parser" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/library/vendor/dompdf/vendor/composer/installed.php b/library/vendor/dompdf/vendor/composer/installed.php new file mode 100644 index 000000000..cdf63de96 --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/installed.php @@ -0,0 +1,68 @@ + array( + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'type' => 'project', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'reference' => NULL, + 'name' => '__root__', + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'type' => 'project', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'reference' => NULL, + 'dev_requirement' => false, + ), + 'dompdf/dompdf' => array( + 'pretty_version' => 'v2.0.1', + 'version' => '2.0.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../dompdf/dompdf', + 'aliases' => array(), + 'reference' => 'c5310df0e22c758c85ea5288175fc6cd777bc085', + 'dev_requirement' => false, + ), + 'masterminds/html5' => array( + 'pretty_version' => '2.7.6', + 'version' => '2.7.6.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../masterminds/html5', + 'aliases' => array(), + 'reference' => '897eb517a343a2281f11bc5556d6548db7d93947', + 'dev_requirement' => false, + ), + 'phenx/php-font-lib' => array( + 'pretty_version' => '0.5.4', + 'version' => '0.5.4.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phenx/php-font-lib', + 'aliases' => array(), + 'reference' => 'dd448ad1ce34c63d09baccd05415e361300c35b4', + 'dev_requirement' => false, + ), + 'phenx/php-svg-lib' => array( + 'pretty_version' => '0.5.0', + 'version' => '0.5.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phenx/php-svg-lib', + 'aliases' => array(), + 'reference' => '76876c6cf3080bcb6f249d7d59705108166a6685', + 'dev_requirement' => false, + ), + 'sabberworm/php-css-parser' => array( + 'pretty_version' => '8.4.0', + 'version' => '8.4.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../sabberworm/php-css-parser', + 'aliases' => array(), + 'reference' => 'e41d2140031d533348b2192a83f02d8dd8a71d30', + 'dev_requirement' => false, + ), + ), +); diff --git a/library/vendor/dompdf/vendor/composer/platform_check.php b/library/vendor/dompdf/vendor/composer/platform_check.php new file mode 100644 index 000000000..6d3407dbb --- /dev/null +++ b/library/vendor/dompdf/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 70100)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/AUTHORS.md b/library/vendor/dompdf/vendor/dompdf/dompdf/AUTHORS.md new file mode 100644 index 000000000..686147928 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/AUTHORS.md @@ -0,0 +1,24 @@ +Dompdf was designed and developed by Benj Carson. + +### Current Team + +* **Brian Sweeney** (maintainer) +* **Till Berger** + +### Alumni + +* **Benj Carson** (creator) +* **Fabien Ménager** +* **Simon Berger** +* **Orion Richardson** + +### Contributors +* **Gabriel Bull** +* **Barry vd. Heuvel** +* **Ryan H. Masten** +* **Helmut Tischer** +* [and many more...](https://github.com/dompdf/dompdf/graphs/contributors) + +### Thanks + +Dompdf would not have been possible without strong community support. diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/LICENSE.LGPL b/library/vendor/dompdf/vendor/dompdf/dompdf/LICENSE.LGPL new file mode 100644 index 000000000..6ef5de82a --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/LICENSE.LGPL @@ -0,0 +1,456 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. \ No newline at end of file diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/README.md b/library/vendor/dompdf/vendor/dompdf/dompdf/README.md new file mode 100644 index 000000000..7546e807e --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/README.md @@ -0,0 +1,232 @@ +Dompdf +====== + +[![Build Status](https://github.com/dompdf/dompdf/actions/workflows/test.yml/badge.svg)](https://github.com/dompdf/dompdf/actions/workflows/test.yml) +[![Latest Release](https://poser.pugx.org/dompdf/dompdf/v/stable.png)](https://packagist.org/packages/dompdf/dompdf) +[![Total Downloads](https://poser.pugx.org/dompdf/dompdf/downloads.png)](https://packagist.org/packages/dompdf/dompdf) +[![License](https://poser.pugx.org/dompdf/dompdf/license.png)](https://packagist.org/packages/dompdf/dompdf) + +**Dompdf is an HTML to PDF converter** + +At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant +HTML layout and rendering engine written in PHP. It is a style-driven renderer: +it will download and read external stylesheets, inline style tags, and the style +attributes of individual HTML elements. It also supports most presentational +HTML attributes. + +*This document applies to the latest stable code which may not reflect the current +release. For released code please +[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).* + +---- + +**Check out the [demo](http://eclecticgeek.com/dompdf/debug.php) and ask any +question on [StackOverflow](https://stackoverflow.com/questions/tagged/dompdf) or +in [Discussions](https://github.com/dompdf/dompdf/discussions).** + +Follow us on [![Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf). + +--- + + + +## Features + + * Handles most CSS 2.1 and a few CSS3 properties, including @import, @media & + @page rules + * Supports most presentational HTML 4.0 attributes + * Supports external stylesheets, either local or through http/ftp (via + fopen-wrappers) + * Supports complex tables, including row & column spans, separate & collapsed + border models, individual cell styling + * Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg) + * No dependencies on external PDF libraries, thanks to the R&OS PDF class + * Inline PHP support + * Basic SVG support (see "Limitations" below) + +## Requirements + + * PHP version 7.1 or higher + * DOM extension + * MBString extension + * php-font-lib + * php-svg-lib + +Note that some required dependencies may have further dependencies +(notably php-svg-lib requires sabberworm/php-css-parser). + +### Recommendations + + * OPcache (OPcache, XCache, APC, etc.): improves performance + * GD (for image processing) + * IMagick or GMagick extension: improves image processing performance + +Visit the wiki for more information: +https://github.com/dompdf/dompdf/wiki/Requirements + +## About Fonts & Character Encoding + +PDF documents internally support the following fonts: Helvetica, Times-Roman, +Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI +encoding. In order for a PDF to display characters that are not available in +Windows ANSI, you must supply an external font. Dompdf will embed any referenced +font in the PDF so long as it has been pre-loaded or is accessible to dompdf and +reference in CSS @font-face rules. See the +[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding) +for more information on how to use fonts. + +The [DejaVu TrueType fonts](https://dejavu-fonts.github.io/) have been pre-installed +to give dompdf decent Unicode character coverage by default. To use the DejaVu +fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu +Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available: +DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono. + +## Easy Installation + +### Install with composer + +To install with [Composer](https://getcomposer.org/), simply require the +latest version of this package. + +```bash +composer require dompdf/dompdf +``` + +Make sure that the autoload file from Composer is loaded. + +```php +// somewhere early in your project's loading, require the Composer autoloader +// see: http://getcomposer.org/doc/00-intro.md +require 'vendor/autoload.php'; + +``` + +### Download and install + +Download a packaged archive of dompdf and extract it into the +directory where dompdf will reside + + * You can download stable copies of dompdf from + https://github.com/dompdf/dompdf/releases + * Or download a nightly (the latest, unreleased code) from + http://eclecticgeek.com/dompdf + +Use the packaged release autoloader to load dompdf, libraries, +and helper functions in your PHP: + +```php +// include autoloader +require_once 'dompdf/autoload.inc.php'; +``` + +Note: packaged releases are named according using semantic +versioning (_dompdf_MAJOR-MINOR-PATCH.zip_). So the 1.0.0 +release would be dompdf_1-0-0.zip. This is the only download +that includes the autoloader for Dompdf and all its dependencies. + +### Install with git + +From the command line, switch to the directory where dompdf will +reside and run the following commands: + +```sh +git clone https://github.com/dompdf/dompdf.git +cd dompdf/lib + +git clone https://github.com/PhenX/php-font-lib.git php-font-lib +cd php-font-lib +git checkout 0.5.1 +cd .. + +git clone https://github.com/PhenX/php-svg-lib.git php-svg-lib +cd php-svg-lib +git checkout v0.3.2 +cd .. + +git clone https://github.com/sabberworm/PHP-CSS-Parser.git php-css-parser +cd php-css-parser +git checkout 8.1.0 +``` + +Require dompdf and it's dependencies in your PHP. +For details see the [autoloader in the utils project](https://github.com/dompdf/utils/blob/master/autoload.inc.php). + +## Quick Start + +Just pass your HTML in to dompdf and stream the output: + +```php +// reference the Dompdf namespace +use Dompdf\Dompdf; + +// instantiate and use the dompdf class +$dompdf = new Dompdf(); +$dompdf->loadHtml('hello world'); + +// (Optional) Setup the paper size and orientation +$dompdf->setPaper('A4', 'landscape'); + +// Render the HTML as PDF +$dompdf->render(); + +// Output the generated PDF to Browser +$dompdf->stream(); +``` + +### Setting Options + +Set options during dompdf instantiation: + +```php +use Dompdf\Dompdf; +use Dompdf\Options; + +$options = new Options(); +$options->set('defaultFont', 'Courier'); +$dompdf = new Dompdf($options); +``` + +or at run time + +```php +use Dompdf\Dompdf; + +$dompdf = new Dompdf(); +$options = $dompdf->getOptions(); +$options->setDefaultFont('Courier'); +$dompdf->setOptions($options); +``` + +See [Dompdf\Options](src/Options.php) for a list of available options. + +### Resource Reference Requirements + +In order to protect potentially sensitive information Dompdf imposes +restrictions on files referenced from the local file system or the web. + +Files accessed through web-based protocols have the following requirements: + * The Dompdf option "isRemoteEnabled" must be set to "true" + * PHP must either have the curl extension enabled or the + allow_url_fopen setting set to true + +Files accessed through the local file system have the following requirement: + * The file must fall within the path(s) specified for the Dompdf "chroot" option + +## Limitations (Known Issues) + + * Table cells are not pageable, meaning a table row must fit on a single page. + * Elements are rendered on the active page when they are parsed. + * Embedding "raw" SVG's (``) isn't working yet, you need to + either link to an external SVG file, or use a DataURI like this: + ```php + $html = ''; + ``` + Watch https://github.com/dompdf/dompdf/issues/320 for progress + * Does not support CSS flexbox. + * Does not support CSS Grid. +--- + +[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf) + +*If you find this project useful, please consider making a donation. +Any funds donated will be used to help further development on this project.)* diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/VERSION b/library/vendor/dompdf/vendor/dompdf/dompdf/VERSION new file mode 100644 index 000000000..38f77a65b --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/VERSION @@ -0,0 +1 @@ +2.0.1 diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/composer.json b/library/vendor/dompdf/vendor/dompdf/dompdf/composer.json new file mode 100644 index 000000000..268a81e7b --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/composer.json @@ -0,0 +1,47 @@ +{ + "name": "dompdf/dompdf", + "type": "library", + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "license": "LGPL-2.1", + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "autoload-dev": { + "psr-4": { + "Dompdf\\Tests\\": "tests/" + } + }, + "require": { + "php": "^7.1 || ^8.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "phenx/php-font-lib": ">=0.5.4 <1.0.0", + "phenx/php-svg-lib": ">=0.3.3 <1.0.0" + }, + "require-dev": { + "ext-json": "*", + "ext-zip": "*", + "phpunit/phpunit": "^7.5 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3.5", + "mockery/mockery": "^1.3" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-imagick": "Improves image processing performance", + "ext-gmagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + } +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/Cpdf.php similarity index 60% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/Cpdf.php index 2dce8f395..27bfa2981 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/Cpdf.php @@ -3,23 +3,41 @@ * A PHP class to provide the basic functionality to create a pdf document without * any requirement for additional modules. * - * Extended by Orion Richardson to support Unicode / UTF-8 characters using - * TCPDF and others as a guide. - * - * @author Wayne Munro - * @author Orion Richardson - * @author Helmut Tischer - * @author Ryan H. Masten - * @author Brian Sweeney - * @author Fabien Ménager - * @license Public Domain http://creativecommons.org/licenses/publicdomain/ + * @author Wayne Munro + * @license http://creativecommons.org/licenses/publicdomain/ Public Domain * @package Cpdf */ +namespace Dompdf; -namespace Svg\Surface; +use FontLib\Exception\FontNotFoundException; +use FontLib\Font; +use FontLib\BinaryStream; -class CPdf +class Cpdf { + const PDF_VERSION = '1.7'; + + const ACROFORM_SIG_SIGNATURESEXISTS = 0x0001; + const ACROFORM_SIG_APPENDONLY = 0x0002; + + const ACROFORM_FIELD_BUTTON = 'Btn'; + const ACROFORM_FIELD_TEXT = 'Tx'; + const ACROFORM_FIELD_CHOICE = 'Ch'; + const ACROFORM_FIELD_SIG = 'Sig'; + + const ACROFORM_FIELD_READONLY = 0x0001; + const ACROFORM_FIELD_REQUIRED = 0x0002; + + const ACROFORM_FIELD_TEXT_MULTILINE = 0x1000; + const ACROFORM_FIELD_TEXT_PASSWORD = 0x2000; + const ACROFORM_FIELD_TEXT_RICHTEXT = 0x10000; + + const ACROFORM_FIELD_CHOICE_COMBO = 0x20000; + const ACROFORM_FIELD_CHOICE_EDIT = 0x40000; + const ACROFORM_FIELD_CHOICE_SORT = 0x80000; + const ACROFORM_FIELD_CHOICE_MULTISELECT = 0x200000; + + const XOBJECT_SUBTYPE_FORM = 'Form'; /** * @var integer The current number of pdf objects in the document @@ -29,18 +47,40 @@ class CPdf /** * @var array This array contains all of the pdf objects, ready for final assembly */ - public $objects = array(); + public $objects = []; /** * @var integer The objectId (number within the objects array) of the document catalog */ public $catalogId; + /** + * @var integer The objectId (number within the objects array) of indirect references (Javascript EmbeddedFiles) + */ + protected $indirectReferenceId = 0; + + /** + * @var integer The objectId (number within the objects array) + */ + protected $embeddedFilesId = 0; + + /** + * AcroForm objectId + * + * @var integer + */ + public $acroFormId; + + /** + * @var int + */ + public $signatureMaxLen = 5000; + /** * @var array Array carrying information about the fonts that the system currently knows about * Used to ensure that a font is not loaded twice, among other things */ - public $fonts = array(); + public $fonts = []; /** * @var string The default font metrics file to use if no other font has been loaded. @@ -88,22 +128,27 @@ class CPdf */ private $numStates = 0; + /** + * @var array Number of graphic state resources used + */ + private $gstates = []; + /** * @var array Current color for fill operations, defaults to inactive value, * all three components should be between 0 and 1 inclusive when active */ public $currentColor = null; - /** - * @var string Fill rule (nonzero or evenodd) - */ - public $fillRule = "nonzero"; - /** * @var array Current color for stroke operations (lines etc.) */ public $currentStrokeColor = null; + /** + * @var string Fill rule (nonzero or evenodd) + */ + public $fillRule = "nonzero"; + /** * @var string Current style that lines are drawn in */ @@ -112,18 +157,18 @@ class CPdf /** * @var array Current line transparency (partial graphics state) */ - public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0); + public $currentLineTransparency = ["mode" => "Normal", "opacity" => 1.0]; /** * array Current fill transparency (partial graphics state) */ - public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0); + public $currentFillTransparency = ["mode" => "Normal", "opacity" => 1.0]; /** * @var array An array which is used to save the state of the document, mainly the colors and styles - * it is used to temporarily change to another state, the change back to what it was before + * it is used to temporarily change to another state, then change back to what it was before */ - public $stateStack = array(); + public $stateStack = []; /** * @var integer Number of elements within the state stack @@ -138,7 +183,7 @@ class CPdf /** * @var array Object Id storage stack */ - public $stack = array(); + public $stack = []; /** * @var integer Number of elements within the object Id storage stack @@ -149,12 +194,12 @@ class CPdf * an array which contains information about the objects which are not firmly attached to pages * these have been added with the addObject function */ - public $looseObjects = array(); + public $looseObjects = []; /** - * array contains infomation about how the loose objects are to be added to the document + * array contains information about how the loose objects are to be added to the document */ - public $addLooseObjects = array(); + public $addLooseObjects = []; /** * @var integer The objectId of the information object for the document @@ -171,25 +216,13 @@ class CPdf * @var array An array containing options about the document * it defaults to turning on the compression of the objects */ - public $options = array('compression' => true); + public $options = ['compression' => true]; /** * @var integer The objectId of the first page of the document */ public $firstPageId; - /** - * @var float Used to track the last used value of the inter-word spacing, this is so that it is known - * when the spacing is changed. - */ - public $wordSpaceAdjust = 0; - - /** - * @var float Used to track the last used value of the inter-letter spacing, this is so that it is known - * when the spacing is changed. - */ - public $charSpaceAdjust = 0; - /** * @var integer The object Id of the procset object */ @@ -198,9 +231,9 @@ class CPdf /** * @var array Store the information about the relationship between font families * this used so that the code knows which font is the bold version of another font, etc. - * the value of this array is initialised in the constuctor function. + * the value of this array is initialised in the constructor function. */ - public $fontFamilies = array(); + public $fontFamilies = []; /** * @var string Folder for php serialized formats of font metrics files. @@ -219,9 +252,8 @@ class CPdf /** * @var string Temporary folder. - * If empty string, will attempty system tmp folder. + * If empty string, will attempt system tmp folder. * This can be passed in from class creator. - * Only used for conversion of gd images to jpeg images. */ public $tmp = ''; @@ -236,7 +268,7 @@ class CPdf public $messages = ''; /** - * @var string The ancryption array for the document encryption is stored here + * @var string The encryption array for the document encryption is stored here */ public $arc4 = ''; @@ -263,7 +295,7 @@ class CPdf /** * @var array Array which forms a stack to keep track of nested callback functions */ - public $callback = array(); + public $callback = []; /** * @var integer The number of callback functions in the callback array @@ -274,7 +306,7 @@ class CPdf * @var array Store label->id pairs for named destinations, these will be used to replace internal links * done this way so that destinations can be defined after the location that links to them */ - public $destinations = array(); + public $destinations = []; /** * @var array Store the stack for the transaction commands, each item in here is a record of the values of all the @@ -287,7 +319,17 @@ class CPdf * @var array Table of Image origin filenames and image labels which were already added with o_image(). * Allows to merge identical images */ - public $imagelist = array(); + public $imagelist = []; + + /** + * @var array Table of already added alpha and plain image files for transparent PNG images. + */ + protected $imageAlphaList = []; + + /** + * @var array List of temporary image files to be deleted after processing. + */ + protected $imageCache = []; /** * @var boolean Whether the text passed in should be treated as Unicode or just local character set. @@ -307,22 +349,27 @@ class CPdf /** * @var array Current page size */ - protected $currentPageSize = array("width" => 0, "height" => 0); + protected $currentPageSize = ["width" => 0, "height" => 0]; /** * @var array All the chars that will be required in the font subsets */ - protected $stringSubsets = array(); + protected $stringSubsets = []; /** * @var string The target internal encoding */ - static protected $targetEncoding = 'iso-8859-1'; + protected static $targetEncoding = 'Windows-1252'; + + /** + * @var array + */ + protected $byteRange = array(); /** * @var array The list of the core fonts */ - static protected $coreFonts = array( + protected static $coreFonts = [ 'courier', 'courier-bold', 'courier-oblique', @@ -337,7 +384,7 @@ class CPdf 'times-bolditalic', 'symbol', 'zapfdingbats' - ); + ]; /** * Class constructor @@ -348,11 +395,11 @@ class CPdf * @param string $fontcache The font cache folder * @param string $tmp The temporary folder */ - function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '') + function __construct($pageSize = [0, 0, 612, 792], $isUnicode = false, $fontcache = '', $tmp = '') { $this->isUnicode = $isUnicode; - $this->fontcache = $fontcache; - $this->tmp = $tmp; + $this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\"); + $this->tmp = ($tmp !== '' ? $tmp : sys_get_temp_dir()); $this->newDocument($pageSize); $this->compressionReady = function_exists('gzcompress'); @@ -363,7 +410,15 @@ class CPdf // also initialize the font families that are known about already $this->setFontFamily('init'); - // $this->fileIdentifier = md5('xxxxxxxx'.time()); + } + + public function __destruct() + { + foreach ($this->imageCache as $file) { + if (file_exists($file)) { + unlink($file); + } + } } /** @@ -383,24 +438,27 @@ class CPdf /** * Destination object, used to specify the location for the user to jump to, presently on opening + * + * @param $id + * @param $action + * @param string $options + * @return string|null */ protected function o_destination($id, $action, $options = '') { - if ($action !== 'new') { - $o = &$this->objects[$id]; - } - switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'destination', 'info' => array()); + $this->objects[$id] = ['t' => 'destination', 'info' => []]; $tmp = ''; switch ($options['type']) { case 'XYZ': + /** @noinspection PhpMissingBreakStatementInspection */ case 'FitR': $tmp = ' ' . $options['p3'] . $tmp; case 'FitH': case 'FitV': case 'FitBH': + /** @noinspection PhpMissingBreakStatementInspection */ case 'FitBV': $tmp = ' ' . $options['p1'] . ' ' . $options['p2'] . $tmp; case 'Fit': @@ -412,56 +470,133 @@ class CPdf break; case 'out': + $o = &$this->objects[$id]; + $tmp = $o['info']; $res = "\n$id 0 obj\n" . '[' . $tmp['page'] . ' 0 R /' . $tmp['string'] . "]\nendobj"; return $res; } + + return null; } /** * set the viewer preferences + * + * @param $id + * @param $action + * @param string|array $options + * @return string|null */ protected function o_viewerPreferences($id, $action, $options = '') { - if ($action !== 'new') { - $o = &$this->objects[$id]; - } - switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array()); + $this->objects[$id] = ['t' => 'viewerPreferences', 'info' => []]; break; case 'add': + $o = &$this->objects[$id]; + foreach ($options as $k => $v) { switch ($k) { + // Boolean keys case 'HideToolbar': case 'HideMenubar': case 'HideWindowUI': case 'FitWindow': case 'CenterWindow': + case 'DisplayDocTitle': + case 'PickTrayByPDFSize': + $o['info'][$k] = (bool)$v; + break; + + // Integer keys + case 'NumCopies': + $o['info'][$k] = (int)$v; + break; + + // Name keys + case 'ViewArea': + case 'ViewClip': + case 'PrintClip': + case 'PrintArea': + $o['info'][$k] = (string)$v; + break; + + // Named with limited valid values case 'NonFullScreenPageMode': - case 'Direction': + if (!in_array($v, ['UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'])) { + break; + } $o['info'][$k] = $v; break; + + case 'Direction': + if (!in_array($v, ['L2R', 'R2L'])) { + break; + } + $o['info'][$k] = $v; + break; + + case 'PrintScaling': + if (!in_array($v, ['None', 'AppDefault'])) { + break; + } + $o['info'][$k] = $v; + break; + + case 'Duplex': + if (!in_array($v, ['None', 'Simplex', 'DuplexFlipShortEdge', 'DuplexFlipLongEdge'])) { + break; + } + $o['info'][$k] = $v; + break; + + // Integer array + case 'PrintPageRange': + // Cast to integer array + foreach ($v as $vK => $vV) { + $v[$vK] = (int)$vV; + } + $o['info'][$k] = array_values($v); + break; } } break; case 'out': + $o = &$this->objects[$id]; $res = "\n$id 0 obj\n<< "; + foreach ($o['info'] as $k => $v) { + if (is_string($v)) { + $v = '/' . $v; + } elseif (is_int($v)) { + $v = (string) $v; + } elseif (is_bool($v)) { + $v = ($v ? 'true' : 'false'); + } elseif (is_array($v)) { + $v = '[' . implode(' ', $v) . ']'; + } $res .= "\n/$k $v"; } - $res .= "\n>>\n"; + $res .= "\n>>\nendobj"; return $res; } + + return null; } /** * define the document catalog, the overall controller for the document + * + * @param $id + * @param $action + * @param string|array $options + * @return string|null */ protected function o_catalog($id, $action, $options = '') { @@ -471,14 +606,15 @@ class CPdf switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'catalog', 'info' => array()); + $this->objects[$id] = ['t' => 'catalog', 'info' => []]; $this->catalogId = $id; break; + case 'acroform': case 'outlines': case 'pages': case 'openHere': - case 'javascript': + case 'names': $o['info'][$action] = $options; break; @@ -515,8 +651,12 @@ class CPdf $res .= "\n/OpenAction $v 0 R"; break; - case 'javascript': - $res .= "\n/Names <>"; + case 'names': + $res .= "\n/Names $v 0 R"; + break; + + case 'acroform': + $res .= "\n/AcroForm $v 0 R"; break; } } @@ -525,10 +665,17 @@ class CPdf return $res; } + + return null; } /** * object which is a parent to the pages in the document + * + * @param $id + * @param $action + * @param string $options + * @return string|null */ protected function o_pages($id, $action, $options = '') { @@ -538,7 +685,7 @@ class CPdf switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'pages', 'info' => array()); + $this->objects[$id] = ['t' => 'pages', 'info' => []]; $this->o_catalog($this->catalogId, 'pages', $id); break; @@ -588,19 +735,19 @@ class CPdf case 'mediaBox': $o['info']['mediaBox'] = $options; // which should be an array of 4 numbers - $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]); + $this->currentPageSize = ['width' => $options[2], 'height' => $options[3]]; break; case 'font': - $o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']); + $o['info']['fonts'][] = ['objNum' => $options['objNum'], 'fontNum' => $options['fontNum']]; break; case 'extGState': - $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']); + $o['info']['extGStates'][] = ['objNum' => $options['objNum'], 'stateNum' => $options['stateNum']]; break; case 'xObject': - $o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']); + $o['info']['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']]; break; case 'out': @@ -666,10 +813,17 @@ class CPdf return $res; } + + return null; } /** * define the outlines in the doc, empty for now + * + * @param $id + * @param $action + * @param string $options + * @return string|null */ protected function o_outlines($id, $action, $options = '') { @@ -679,7 +833,7 @@ class CPdf switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array())); + $this->objects[$id] = ['t' => 'outlines', 'info' => ['outlines' => []]]; $this->o_catalog($this->catalogId, 'outlines', $id); break; @@ -701,10 +855,18 @@ class CPdf return $res; } + + return null; } /** * an object to hold the font description + * + * @param $id + * @param $action + * @param string|array $options + * @return string|null + * @throws FontNotFoundException */ protected function o_font($id, $action, $options = '') { @@ -714,14 +876,15 @@ class CPdf switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'font', - 'info' => array( + 'info' => [ 'name' => $options['name'], 'fontFileName' => $options['fontFileName'], - 'SubType' => 'Type1' - ) - ); + 'SubType' => 'Type1', + 'isSubsetting' => $options['isSubsetting'] + ] + ]; $fontNum = $this->numFonts; $this->objects[$id]['info']['fontNum'] = $fontNum; @@ -757,72 +920,48 @@ class CPdf // For Unicode fonts, we need to incorporate font data into // sub-sections that are linked from the primary font section. // Look at o_fontGIDtoCID and o_fontDescendentCID functions - // for more informaiton. + // for more information. // // All of this code is adapted from the excellent changes made to // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/) $toUnicodeId = ++$this->numObj; - $this->o_contents($toUnicodeId, 'new', 'raw'); + $this->o_toUnicode($toUnicodeId, 'new'); $this->objects[$id]['info']['toUnicode'] = $toUnicodeId; - $stream = <<> def -/CMapName /Adobe-Identity-UCS def -/CMapType 2 def -1 begincodespacerange -<0000> -endcodespacerange -1 beginbfrange -<0000> <0000> -endbfrange -endcmap -CMapName currentdict /CMap defineresource pop -end -end -EOT; - - $res = "<>\n"; - $res .= "stream\n" . $stream . "endstream"; - - $this->objects[$toUnicodeId]['c'] = $res; - $cidFontId = ++$this->numObj; $this->o_fontDescendentCID($cidFontId, 'new', $options); $this->objects[$id]['info']['cidFont'] = $cidFontId; } // also tell the pages node about the new font - $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id)); + $this->o_pages($this->currentNode, 'font', ['fontNum' => $fontNum, 'objNum' => $id]); break; case 'add': - foreach ($options as $k => $v) { - switch ($k) { - case 'BaseFont': - $o['info']['name'] = $v; - break; - case 'FirstChar': - case 'LastChar': - case 'Widths': - case 'FontDescriptor': - case 'SubType': - $this->addMessage('o_font ' . $k . " : " . $v); - $o['info'][$k] = $v; - break; - } - } + $font_options = $this->processFont($id, $o['info']); - // pass values down to descendent font - if (isset($o['info']['cidFont'])) { - $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options); + if ($font_options !== false) { + foreach ($font_options as $k => $v) { + switch ($k) { + case 'BaseFont': + $o['info']['name'] = $v; + break; + case 'FirstChar': + case 'LastChar': + case 'Widths': + case 'FontDescriptor': + case 'SubType': + $this->addMessage('o_font ' . $k . " : " . $v); + $o['info'][$k] = $v; + break; + } + } + + // pass values down to descendent font + if (isset($o['info']['cidFont'])) { + $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $font_options); + } } break; @@ -831,7 +970,7 @@ EOT; // For Unicode fonts, we need to incorporate font data into // sub-sections that are linked from the primary font section. // Look at o_fontGIDtoCID and o_fontDescendentCID functions - // for more informaiton. + // for more information. // // All of this code is adapted from the excellent changes made to // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/) @@ -883,10 +1022,371 @@ EOT; return $res; } + + return null; + } + + protected function getFontSubsettingTag(array $font): string + { + // convert font num to hexavigesimal numeral system letters A - Z only + $base_26 = strtoupper(base_convert($font['fontNum'], 10, 26)); + for ($i = 0; $i < strlen($base_26); $i++) { + $char = $base_26[$i]; + if ($char <= "9") { + $base_26[$i] = chr(65 + intval($char)); + } else { + $base_26[$i] = chr(ord($char) + 10); + } + } + + return 'SUB' . str_pad($base_26, 3, 'A', STR_PAD_LEFT); + } + + /** + * @param int $fontObjId + * @param array $object_info + * @return array|false + * @throws FontNotFoundException + */ + private function processFont(int $fontObjId, array $object_info) + { + $fontFileName = $object_info['fontFileName']; + if (!isset($this->fonts[$fontFileName])) { + return false; + } + + $font = &$this->fonts[$fontFileName]; + + $fileSuffix = $font['fileSuffix']; + $fileSuffixLower = strtolower($font['fileSuffix']); + $fbfile = "$fontFileName.$fileSuffix"; + $isTtfFont = $fileSuffixLower === 'ttf'; + $isPfbFont = $fileSuffixLower === 'pfb'; + + $this->addMessage('selectFont: checking for - ' . $fbfile); + + if (!$fileSuffix) { + $this->addMessage( + 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts' + ); + + return false; + } else { + $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName']; + // $fontObj = $this->numObj; + $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName"); + + // find the array of font widths, and put that into an object. + $firstChar = -1; + $lastChar = 0; + $widths = []; + $cid_widths = []; + + foreach ($font['C'] as $num => $d) { + if (intval($num) > 0 || $num == '0') { + if (!$font['isUnicode']) { + // With Unicode, widths array isn't used + if ($lastChar > 0 && $num > $lastChar + 1) { + for ($i = $lastChar + 1; $i < $num; $i++) { + $widths[] = 0; + } + } + } + + $widths[] = $d; + + if ($font['isUnicode']) { + $cid_widths[$num] = $d; + } + + if ($firstChar == -1) { + $firstChar = $num; + } + + $lastChar = $num; + } + } + + // also need to adjust the widths for the differences array + if (isset($object['differences'])) { + foreach ($object['differences'] as $charNum => $charName) { + if ($charNum > $lastChar) { + if (!$object['isUnicode']) { + // With Unicode, widths array isn't used + for ($i = $lastChar + 1; $i <= $charNum; $i++) { + $widths[] = 0; + } + } + + $lastChar = $charNum; + } + + if (isset($font['C'][$charName])) { + $widths[$charNum - $firstChar] = $font['C'][$charName]; + if ($font['isUnicode']) { + $cid_widths[$charName] = $font['C'][$charName]; + } + } + } + } + + if ($font['isUnicode']) { + $font['CIDWidths'] = $cid_widths; + } + + $this->addMessage('selectFont: FirstChar = ' . $firstChar); + $this->addMessage('selectFont: LastChar = ' . $lastChar); + + $widthid = -1; + + if (!$font['isUnicode']) { + // With Unicode, widths array isn't used + + $this->numObj++; + $this->o_contents($this->numObj, 'new', 'raw'); + $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']'; + $widthid = $this->numObj; + } + + $missing_width = 500; + $stemV = 70; + + if (isset($font['MissingWidth'])) { + $missing_width = $font['MissingWidth']; + } + if (isset($font['StdVW'])) { + $stemV = $font['StdVW']; + } else { + if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) { + $stemV = 120; + } + } + + // load the pfb file, and put that into an object too. + // note that pdf supports only binary format type 1 font files, though there is a + // simple utility to convert them from pfa to pfb. + if (!$font['isSubsetting']) { + $data = file_get_contents($fbfile); + } else { + $adobeFontName = $this->getFontSubsettingTag($font) . '+' . $adobeFontName; + $this->stringSubsets[$fontFileName][] = 32; // Force space if not in yet + + $subset = $this->stringSubsets[$fontFileName]; + sort($subset); + + // Load font + $font_obj = Font::load($fbfile); + $font_obj->parse(); + + // Define subset + $font_obj->setSubset($subset); + $font_obj->reduce(); + + // Write new font + $tmp_name = @tempnam($this->tmp, "cpdf_subset_"); + $font_obj->open($tmp_name, BinaryStream::modeReadWrite); + $font_obj->encode(["OS/2"]); + $font_obj->close(); + + // Parse the new font to get cid2gid and widths + $font_obj = Font::load($tmp_name); + + // Find Unicode char map table + $subtable = null; + foreach ($font_obj->getData("cmap", "subtables") as $_subtable) { + if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) { + $subtable = $_subtable; + break; + } + } + + if ($subtable) { + $glyphIndexArray = $subtable["glyphIndexArray"]; + $hmtx = $font_obj->getData("hmtx"); + + unset($glyphIndexArray[0xFFFF]); + + $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00"); + $font['CIDWidths'] = []; + foreach ($glyphIndexArray as $cid => $gid) { + if ($cid >= 0 && $cid < 0xFFFF && $gid) { + $cidtogid[$cid * 2] = chr($gid >> 8); + $cidtogid[$cid * 2 + 1] = chr($gid & 0xFF); + } + + $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]); + $font['CIDWidths'][$cid] = $width; + } + + $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid)); + $font['CIDtoGID_Compressed'] = true; + + $data = file_get_contents($tmp_name); + } else { + $data = file_get_contents($fbfile); + } + + $font_obj->close(); + unlink($tmp_name); + } + + // create the font descriptor + $this->numObj++; + $fontDescriptorId = $this->numObj; + + $this->numObj++; + $pfbid = $this->numObj; + + // determine flags (more than a little flakey, hopefully will not matter much) + $flags = 0; + + if ($font['ItalicAngle'] != 0) { + $flags += pow(2, 6); + } + + if ($font['IsFixedPitch'] === 'true') { + $flags += 1; + } + + $flags += pow(2, 5); // assume non-sybolic + $list = [ + 'Ascent' => 'Ascender', + 'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight' + 'MissingWidth' => 'MissingWidth', + 'Descent' => 'Descender', + 'FontBBox' => 'FontBBox', + 'ItalicAngle' => 'ItalicAngle' + ]; + $fdopt = [ + 'Flags' => $flags, + 'FontName' => $adobeFontName, + 'StemV' => $stemV + ]; + + foreach ($list as $k => $v) { + if (isset($font[$v])) { + $fdopt[$k] = $font[$v]; + } + } + + if ($isPfbFont) { + $fdopt['FontFile'] = $pfbid; + } elseif ($isTtfFont) { + $fdopt['FontFile2'] = $pfbid; + } + + $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt); + + // embed the font program + $this->o_contents($this->numObj, 'new'); + $this->objects[$pfbid]['c'] .= $data; + + // determine the cruicial lengths within this file + if ($isPfbFont) { + $l1 = strpos($data, 'eexec') + 6; + $l2 = strpos($data, '00000000') - $l1; + $l3 = mb_strlen($data, '8bit') - $l2 - $l1; + $this->o_contents( + $this->numObj, + 'add', + ['Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3] + ); + } elseif ($isTtfFont) { + $l1 = mb_strlen($data, '8bit'); + $this->o_contents($this->numObj, 'add', ['Length1' => $l1]); + } + + // tell the font object about all this new stuff + $options = [ + 'BaseFont' => $adobeFontName, + 'MissingWidth' => $missing_width, + 'Widths' => $widthid, + 'FirstChar' => $firstChar, + 'LastChar' => $lastChar, + 'FontDescriptor' => $fontDescriptorId + ]; + + if ($isTtfFont) { + $options['SubType'] = 'TrueType'; + } + + $this->addMessage("adding extra info to font.($fontObjId)"); + + foreach ($options as $fk => $fv) { + $this->addMessage("$fk : $fv"); + } + } + + return $options; + } + + /** + * A toUnicode section, needed for unicode fonts + * + * @param $id + * @param $action + * @return null|string + */ + protected function o_toUnicode($id, $action) + { + switch ($action) { + case 'new': + $this->objects[$id] = [ + 't' => 'toUnicode' + ]; + break; + case 'add': + break; + case 'out': + $ordering = 'UCS'; + $registry = 'Adobe'; + + if ($this->encrypted) { + $this->encryptInit($id); + $ordering = $this->ARC4($ordering); + $registry = $this->filterText($this->ARC4($registry), false, false); + } + + $stream = <<> def +/CMapName /Adobe-Identity-UCS def +/CMapType 2 def +1 begincodespacerange +<0000> +endcodespacerange +1 beginbfrange +<0000> <0000> +endbfrange +endcmap +CMapName currentdict /CMap defineresource pop +end +end +EOT; + + $res = "\n$id 0 obj\n"; + $res .= "<>\n"; + $res .= "stream\n" . $stream . "\nendstream" . "\nendobj"; + + return $res; + } + + return null; } /** * a font descriptor, needed for including additional fonts + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_fontDescriptor($id, $action, $options = '') { @@ -896,7 +1396,7 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options); + $this->objects[$id] = ['t' => 'fontDescriptor', 'info' => $options]; break; case 'out': @@ -941,10 +1441,17 @@ EOT; return $res; } + + return null; } /** * the font encoding + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_fontEncoding($id, $action, $options = '') { @@ -955,7 +1462,7 @@ EOT; switch ($action) { case 'new': // the options array should contain 'differences' and maybe 'encoding' - $this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options); + $this->objects[$id] = ['t' => 'fontEncoding', 'info' => $options]; break; case 'out': @@ -987,10 +1494,17 @@ EOT; return $res; } + + return null; } /** * a descendent cid font, needed for unicode fonts + * + * @param $id + * @param $action + * @param string|array $options + * @return null|string */ protected function o_fontDescendentCID($id, $action, $options = '') { @@ -1000,17 +1514,12 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options); + $this->objects[$id] = ['t' => 'fontDescendentCID', 'info' => $options]; // we need a CID system info section $cidSystemInfoId = ++$this->numObj; - $this->o_contents($cidSystemInfoId, 'new', 'raw'); + $this->o_cidSystemInfo($cidSystemInfoId, 'new'); $this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId; - $res = "<objects[$cidSystemInfoId]['c'] = $res; // and a CID to GID map $cidToGidMapId = ++$this->numObj; @@ -1046,13 +1555,13 @@ EOT; $res .= "/Subtype /CIDFontType2\n"; $res .= "/BaseFont /" . $o['info']['name'] . "\n"; $res .= "/CIDSystemInfo " . $o['info']['cidSystemInfo'] . " 0 R\n"; -// if (isset($o['info']['FirstChar'])) { -// $res.= "/FirstChar ".$o['info']['FirstChar']."\n"; -// } + // if (isset($o['info']['FirstChar'])) { + // $res.= "/FirstChar ".$o['info']['FirstChar']."\n"; + // } -// if (isset($o['info']['LastChar'])) { -// $res.= "/LastChar ".$o['info']['LastChar']."\n"; -// } + // if (isset($o['info']['LastChar'])) { + // $res.= "/LastChar ".$o['info']['LastChar']."\n"; + // } if (isset($o['info']['FontDescriptor'])) { $res .= "/FontDescriptor " . $o['info']['FontDescriptor'] . " 0 R\n"; } @@ -1076,10 +1585,60 @@ EOT; return $res; } + + return null; + } + + /** + * CID system info section, needed for unicode fonts + * + * @param $id + * @param $action + * @return null|string + */ + protected function o_cidSystemInfo($id, $action) + { + switch ($action) { + case 'new': + $this->objects[$id] = [ + 't' => 'cidSystemInfo' + ]; + break; + case 'add': + break; + case 'out': + $ordering = 'UCS'; + $registry = 'Adobe'; + + if ($this->encrypted) { + $this->encryptInit($id); + $ordering = $this->ARC4($ordering); + $registry = $this->ARC4($registry); + } + + + $res = "\n$id 0 obj\n"; + + $res .= '<objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options); + $this->objects[$id] = ['t' => 'fontGIDtoCIDMap', 'info' => $options]; break; case 'out': @@ -1114,6 +1673,11 @@ EOT; $res .= "\n/Filter /FlateDecode"; } + if ($this->encrypted) { + $this->encryptInit($id); + $tmp = $this->ARC4($tmp); + } + $res .= "\n/Length " . mb_strlen($tmp, '8bit') . ">>\nstream\n$tmp\nendstream"; } @@ -1121,10 +1685,17 @@ EOT; return $res; } + + return null; } /** * the document procset, solves some problems with printing to old PS printers + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_procset($id, $action, $options = '') { @@ -1134,14 +1705,14 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1)); + $this->objects[$id] = ['t' => 'procset', 'info' => ['PDF' => 1, 'Text' => 1]]; $this->o_pages($this->currentNode, 'procset', $id); $this->procsetObjectId = $id; break; case 'add': // this is to add new items to the procset list, despite the fact that this is considered - // obselete, the items are required for printing to some postscript printers + // obsolete, the items are required for printing to some postscript printers switch ($options) { case 'ImageB': case 'ImageC': @@ -1160,28 +1731,31 @@ EOT; return $res; } + + return null; } /** * define the document information + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_info($id, $action, $options = '') { - if ($action !== 'new') { - $o = &$this->objects[$id]; - } - switch ($action) { case 'new': $this->infoObject = $id; $date = 'D:' . @date('Ymd'); - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'info', - 'info' => array( - 'Creator' => 'R and OS php pdf writer, http://www.ros.co.nz', + 'info' => [ + 'Producer' => 'CPDF (dompdf)', 'CreationDate' => $date - ) - ); + ] + ]; break; case 'Title': case 'Author': @@ -1192,26 +1766,30 @@ EOT; case 'CreationDate': case 'ModDate': case 'Trapped': - $o['info'][$action] = $options; + $this->objects[$id]['info'][$action] = $options; break; case 'out': - if ($this->encrypted) { + $encrypted = $this->encrypted; + if ($encrypted) { $this->encryptInit($id); } $res = "\n$id 0 obj\n<<\n"; + $o = &$this->objects[$id]; foreach ($o['info'] as $k => $v) { $res .= "/$k ("; - if ($this->encrypted) { - $v = $this->ARC4($v); - } // dates must be outputted as-is, without Unicode transformations - elseif (!in_array($k, array('CreationDate', 'ModDate'))) { - $v = $this->filterText($v); + // dates must be outputted as-is, without Unicode transformations + if ($k !== 'CreationDate' && $k !== 'ModDate') { + $v = $this->utf8toUtf16BE($v); } - $res .= $v; + if ($encrypted) { + $v = $this->ARC4($v); + } + + $res .= $this->filterText($v, false, false); $res .= ")\n"; } @@ -1219,10 +1797,17 @@ EOT; return $res; } + + return null; } /** * an action object, used to link to URLS initially + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_action($id, $action, $options = '') { @@ -1233,10 +1818,10 @@ EOT; switch ($action) { case 'new': if (is_array($options)) { - $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']); + $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => $options['type']]; } else { // then assume a URI action - $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI'); + $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => 'URI']; } break; @@ -1259,9 +1844,9 @@ EOT; case 'URI': $res .= "\n/S /URI\n/URI ("; if ($this->encrypted) { - $res .= $this->filterText($this->ARC4($o['info']), true, false); + $res .= $this->filterText($this->ARC4($o['info']), false, false); } else { - $res .= $this->filterText($o['info'], true, false); + $res .= $this->filterText($o['info'], false, false); } $res .= ")"; @@ -1272,11 +1857,18 @@ EOT; return $res; } + + return null; } /** * an annotation object, this will add an annotation to the current page. * initially will support just link annotations + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_annotation($id, $action, $options = '') { @@ -1293,7 +1885,7 @@ EOT; // and add the action object which is going to be required switch ($options['type']) { case 'link': - $this->objects[$id] = array('t' => 'annotation', 'info' => $options); + $this->objects[$id] = ['t' => 'annotation', 'info' => $options]; $this->numObj++; $this->o_action($this->numObj, 'new', $options['url']); $this->objects[$id]['info']['actionId'] = $this->numObj; @@ -1302,9 +1894,9 @@ EOT; case 'ilink': // this is to a named internal link $label = $options['label']; - $this->objects[$id] = array('t' => 'annotation', 'info' => $options); + $this->objects[$id] = ['t' => 'annotation', 'info' => $options]; $this->numObj++; - $this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label)); + $this->o_action($this->numObj, 'new', ['type' => 'ilink', 'label' => $label]); $this->objects[$id]['info']['actionId'] = $this->numObj; break; } @@ -1332,10 +1924,17 @@ EOT; return $res; } + + return null; } /** * a page object, it also creates a contents object to hold its contents + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_page($id, $action, $options = '') { @@ -1346,13 +1945,14 @@ EOT; switch ($action) { case 'new': $this->numPages++; - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'page', - 'info' => array( + 'info' => [ 'parent' => $this->currentNode, - 'pageNum' => $this->numPages - ) - ); + 'pageNum' => $this->numPages, + 'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox'] + ] + ]; if (is_array($options)) { // then this must be a page insertion, array should contain 'rid','pos'=[before|after] @@ -1367,7 +1967,7 @@ EOT; $this->numObj++; $this->o_contents($this->numObj, 'new', $id); $this->currentContents = $this->numObj; - $this->objects[$id]['info']['contents'] = array(); + $this->objects[$id]['info']['contents'] = []; $this->objects[$id]['info']['contents'][] = $this->numObj; $match = ($this->numPages % 2 ? 'odd' : 'even'); @@ -1385,7 +1985,7 @@ EOT; case 'annot': // add an annotation to this page if (!isset($o['info']['annot'])) { - $o['info']['annot'] = array(); + $o['info']['annot'] = []; } // $options should contain the id of the annotation dictionary @@ -1394,6 +1994,16 @@ EOT; case 'out': $res = "\n$id 0 obj\n<< /Type /Page"; + if (isset($o['info']['mediaBox'])) { + $tmp = $o['info']['mediaBox']; + $res .= "\n/MediaBox [" . sprintf( + '%.3F %.3F %.3F %.3F', + $tmp[0], + $tmp[1], + $tmp[2], + $tmp[3] + ) . ']'; + } $res .= "\n/Parent " . $o['info']['parent'] . " 0 R"; if (isset($o['info']['annot'])) { @@ -1425,10 +2035,17 @@ EOT; return $res; } + + return null; } /** * the contents objects hold all of the content which appears on pages + * + * @param $id + * @param $action + * @param string|array $options + * @return null|string */ protected function o_contents($id, $action, $options = '') { @@ -1438,7 +2055,7 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array()); + $this->objects[$id] = ['t' => 'contents', 'c' => '', 'info' => []]; if (mb_strlen($options, '8bit') && intval($options)) { // then this contents is the primary for a page $this->objects[$id]['onPage'] = $options; @@ -1451,7 +2068,7 @@ EOT; break; case 'add': - // add more options to the decleration + // add more options to the declaration foreach ($options as $k => $v) { $o['info'][$k] = $v; } @@ -1486,25 +2103,29 @@ EOT; return $res; } + + return null; } + /** + * @param $id + * @param $action + * @return string|null + */ protected function o_embedjs($id, $action) { - if ($action !== 'new') { - $o = &$this->objects[$id]; - } - switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'embedjs', - 'info' => array( + 'info' => [ 'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]' - ) - ); + ] + ]; break; case 'out': + $o = &$this->objects[$id]; $res = "\n$id 0 obj\n<< "; foreach ($o['info'] as $k => $v) { $res .= "\n/$k $v"; @@ -1513,27 +2134,33 @@ EOT; return $res; } + + return null; } + /** + * @param $id + * @param $action + * @param string $code + * @return null|string + */ protected function o_javascript($id, $action, $code = '') { - if ($action !== 'new') { - $o = &$this->objects[$id]; - } - switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'javascript', - 'info' => array( + 'info' => [ 'S' => '/JavaScript', - 'JS' => '(' . $this->filterText($code) . ')', - ) - ); + 'JS' => '(' . $this->filterText($code, true, false) . ')', + ] + ]; break; case 'out': + $o = &$this->objects[$id]; $res = "\n$id 0 obj\n<< "; + foreach ($o['info'] as $k => $v) { $res .= "\n/$k $v"; } @@ -1541,21 +2168,24 @@ EOT; return $res; } + + return null; } /** * an image object, will be an XObject in the document, includes description and data + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_image($id, $action, $options = '') { - if ($action !== 'new') { - $o = &$this->objects[$id]; - } - switch ($action) { case 'new': // make the new object - $this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array()); + $this->objects[$id] = ['t' => 'image', 'data' => &$options['data'], 'info' => []]; $info =& $this->objects[$id]['info']; @@ -1574,10 +2204,10 @@ EOT; } switch ($options['channels']) { - case 1: + case 1: $info['ColorSpace'] = '/DeviceGray'; break; - case 4: + case 4: $info['ColorSpace'] = '/DeviceCMYK'; break; default: @@ -1656,13 +2286,14 @@ EOT; // assign it a place in the named resource dictionary as an external object, according to // the label passed in with it. - $this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id)); + $this->o_pages($this->currentNode, 'xObject', ['label' => $options['label'], 'objNum' => $id]); // also make sure that we have the right procset object for it. $this->o_procset($this->procsetObjectId, 'add', 'ImageC'); break; case 'out': + $o = &$this->objects[$id]; $tmp = &$o['data']; $res = "\n$id 0 obj\n<<"; @@ -1679,14 +2310,21 @@ EOT; return $res; } + + return null; } /** * graphics state object + * + * @param $id + * @param $action + * @param string $options + * @return null|string */ protected function o_extGState($id, $action, $options = "") { - static $valid_params = array( + static $valid_params = [ "LW", "LC", "LC", @@ -1713,22 +2351,19 @@ EOT; "ca", "AIS", "TK" - ); - - if ($action !== "new") { - $o = &$this->objects[$id]; - } + ]; switch ($action) { case "new": - $this->objects[$id] = array('t' => 'extGState', 'info' => $options); + $this->objects[$id] = ['t' => 'extGState', 'info' => $options]; // Tell the pages about the new resource $this->numStates++; - $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates)); + $this->o_pages($this->currentNode, 'extGState', ["objNum" => $id, "stateNum" => $this->numStates]); break; case "out": + $o = &$this->objects[$id]; $res = "\n$id 0 obj\n<< /Type /ExtGState\n"; foreach ($o["info"] as $k => $v) { @@ -1742,49 +2377,411 @@ EOT; return $res; } + + return null; + } + + /** + * @param integer $id + * @param string $action + * @param mixed $options + * @return string + */ + protected function o_xobject($id, $action, $options = '') + { + switch ($action) { + case 'new': + $this->objects[$id] = ['t' => 'xobject', 'info' => $options, 'c' => '']; + break; + + case 'procset': + $this->objects[$id]['procset'] = $options; + break; + + case 'font': + $this->objects[$id]['fonts'][$options['fontNum']] = [ + 'objNum' => $options['objNum'], + 'fontNum' => $options['fontNum'] + ]; + break; + + case 'xObject': + $this->objects[$id]['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']]; + break; + + case 'out': + $o = &$this->objects[$id]; + $res = "\n$id 0 obj\n<< /Type /XObject\n"; + + foreach ($o["info"] as $k => $v) { + switch ($k) { + case 'Subtype': + $res .= "/Subtype /$v\n"; + break; + case 'bbox': + $res .= "/BBox ["; + foreach ($v as $value) { + $res .= sprintf("%.4F ", $value); + } + $res .= "]\n"; + break; + default: + $res .= "/$k $v\n"; + break; + } + } + $res .= "/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]\n"; + + $res .= "/Resources <<"; + if (isset($o['procset'])) { + $res .= "\n/ProcSet " . $o['procset'] . " 0 R"; + } else { + $res .= "\n/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]"; + } + if (isset($o['fonts']) && count($o['fonts'])) { + $res .= "\n/Font << "; + foreach ($o['fonts'] as $finfo) { + $res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R"; + } + $res .= "\n>>"; + } + if (isset($o['xObjects']) && count($o['xObjects'])) { + $res .= "\n/XObject << "; + foreach ($o['xObjects'] as $finfo) { + $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R"; + } + $res .= "\n>>"; + } + $res .= "\n>>\n"; + + $tmp = $o["c"]; + if ($this->compressionReady && $this->options['compression']) { + // then implement ZLIB based compression on this content stream + $res .= " /Filter /FlateDecode\n"; + $tmp = gzcompress($tmp, 6); + } + + if ($this->encrypted) { + $this->encryptInit($id); + $tmp = $this->ARC4($tmp); + } + + $res .= "/Length " . mb_strlen($tmp, '8bit') . " >>\n"; + $res .= "stream\n" . $tmp . "\nendstream" . "\nendobj"; + + return $res; + } + + return null; + } + + /** + * @param $id + * @param $action + * @param string $options + * @return null|string + */ + protected function o_acroform($id, $action, $options = '') + { + switch ($action) { + case "new": + $this->o_catalog($this->catalogId, 'acroform', $id); + $this->objects[$id] = array('t' => 'acroform', 'info' => $options); + break; + + case 'addfield': + $this->objects[$id]['info']['Fields'][] = $options; + break; + + case 'font': + $this->objects[$id]['fonts'][$options['fontNum']] = [ + 'objNum' => $options['objNum'], + 'fontNum' => $options['fontNum'] + ]; + break; + + case "out": + $o = &$this->objects[$id]; + $res = "\n$id 0 obj\n<<"; + + foreach ($o["info"] as $k => $v) { + switch ($k) { + case 'Fields': + $res .= " /Fields ["; + foreach ($v as $i) { + $res .= "$i 0 R "; + } + $res .= "]\n"; + break; + default: + $res .= "/$k $v\n"; + } + } + + $res .= "/DR <<\n"; + if (isset($o['fonts']) && count($o['fonts'])) { + $res .= "/Font << \n"; + foreach ($o['fonts'] as $finfo) { + $res .= "/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R\n"; + } + $res .= ">>\n"; + } + $res .= ">>\n"; + + $res .= ">>\nendobj"; + + return $res; + } + + return null; + } + + /** + * @param $id + * @param $action + * @param mixed $options + * @return null|string + */ + protected function o_field($id, $action, $options = '') + { + switch ($action) { + case "new": + $this->o_page($options['pageid'], 'annot', $id); + $this->o_acroform($this->acroFormId, 'addfield', $id); + $this->objects[$id] = ['t' => 'field', 'info' => $options]; + break; + + case 'set': + $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options); + break; + + case "out": + $o = &$this->objects[$id]; + $res = "\n$id 0 obj\n<< /Type /Annot /Subtype /Widget \n"; + + $encrypted = $this->encrypted; + if ($encrypted) { + $this->encryptInit($id); + } + + foreach ($o["info"] as $k => $v) { + switch ($k) { + case 'pageid': + $res .= "/P $v 0 R\n"; + break; + case 'value': + if ($encrypted) { + $v = $this->filterText($this->ARC4($v), false, false); + } + $res .= "/V ($v)\n"; + break; + case 'refvalue': + $res .= "/V $v 0 R\n"; + break; + case 'da': + if ($encrypted) { + $v = $this->filterText($this->ARC4($v), false, false); + } + $res .= "/DA ($v)\n"; + break; + case 'options': + $res .= "/Opt [\n"; + foreach ($v as $opt) { + if ($encrypted) { + $opt = $this->filterText($this->ARC4($opt), false, false); + } + $res .= "($opt)\n"; + } + $res .= "]\n"; + break; + case 'rect': + $res .= "/Rect ["; + foreach ($v as $value) { + $res .= sprintf("%.4F ", $value); + } + $res .= "]\n"; + break; + case 'appearance': + $res .= "/AP << "; + foreach ($v as $a => $ref) { + $res .= "/$a $ref 0 R "; + } + $res .= ">>\n"; + break; + case 'T': + if ($encrypted) { + $v = $this->filterText($this->ARC4($v), false, false); + } + $res .= "/T ($v)\n"; + break; + default: + $res .= "/$k $v\n"; + } + + } + + $res .= ">>\nendobj"; + + return $res; + } + + return null; + } + + /** + * + * @param $id + * @param $action + * @param string $options + * @return null|string + */ + protected function o_sig($id, $action, $options = '') + { + $sign_maxlen = $this->signatureMaxLen; + + switch ($action) { + case "new": + $this->objects[$id] = array('t' => 'sig', 'info' => $options); + $this->byteRange[$id] = ['t' => 'sig']; + break; + + case 'byterange': + $o = &$this->objects[$id]; + $content =& $options['content']; + $content_len = strlen($content); + $pos = strpos($content, sprintf("/ByteRange [ %'.010d", $id)); + $len = strlen('/ByteRange [ ********** ********** ********** ********** ]'); + $rangeStartPos = $pos + $len + 1 + 10; // before '<' + $content = substr_replace($content, str_pad(sprintf('/ByteRange [ 0 %u %u %u ]', $rangeStartPos, $rangeStartPos + $sign_maxlen + 2, $content_len - 2 - $sign_maxlen - $rangeStartPos), $len, ' ', STR_PAD_RIGHT), $pos, $len); + + $fuid = uniqid(); + $tmpInput = $this->tmp . "/pkcs7.tmp." . $fuid . '.in'; + $tmpOutput = $this->tmp . "/pkcs7.tmp." . $fuid . '.out'; + + if (file_put_contents($tmpInput, substr($content, 0, $rangeStartPos)) === false) { + throw new \Exception("Unable to write temporary file for signing."); + } + if (file_put_contents($tmpInput, substr($content, $rangeStartPos + 2 + $sign_maxlen), + FILE_APPEND) === false) { + throw new \Exception("Unable to write temporary file for signing."); + } + + if (openssl_pkcs7_sign($tmpInput, $tmpOutput, + $o['info']['SignCert'], + array($o['info']['PrivKey'], $o['info']['Password']), + array(), PKCS7_BINARY | PKCS7_DETACHED) === false) { + throw new \Exception("Failed to prepare signature."); + } + + $signature = file_get_contents($tmpOutput); + + unlink($tmpInput); + unlink($tmpOutput); + + $sign = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); + list($head, $signature) = explode("\n\n", $sign); + + $signature = base64_decode(trim($signature)); + + $signature = current(unpack('H*', $signature)); + $signature = str_pad($signature, $sign_maxlen, '0'); + $siglen = strlen($signature); + if (strlen($signature) > $sign_maxlen) { + throw new \Exception("Signature length ($siglen) exceeds the $sign_maxlen limit."); + } + + $content = substr_replace($content, $signature, $rangeStartPos + 1, $sign_maxlen); + break; + + case "out": + $res = "\n$id 0 obj\n<<\n"; + + $encrypted = $this->encrypted; + if ($encrypted) { + $this->encryptInit($id); + } + + $res .= "/ByteRange " .sprintf("[ %'.010d ********** ********** ********** ]\n", $id); + $res .= "/Contents <" . str_pad('', $sign_maxlen, '0') . ">\n"; + $res .= "/Filter/Adobe.PPKLite\n"; //PPKMS \n"; + $res .= "/Type/Sig/SubFilter/adbe.pkcs7.detached \n"; + + $date = "D:" . substr_replace(date('YmdHisO'), '\'', -2, 0) . '\''; + if ($encrypted) { + $date = $this->ARC4($date); + } + + $res .= "/M ($date)\n"; + $res .= "/Prop_Build << /App << /Name /DomPDF >> /Filter << /Name /Adobe.PPKLite >> >>\n"; + + $o = &$this->objects[$id]; + foreach ($o['info'] as $k => $v) { + switch ($k) { + case 'Name': + case 'Location': + case 'Reason': + case 'ContactInfo': + if ($v !== null && $v !== '') { + $res .= "/$k (" . + ($encrypted ? $this->filterText($this->ARC4($v), false, false) : $v) . ") \n"; + } + break; + } + } + $res .= ">>\nendobj"; + + return $res; + } + + return null; } /** * encryption object. + * + * @param $id + * @param $action + * @param string $options + * @return string|null */ protected function o_encryption($id, $action, $options = '') { - if ($action !== 'new') { - $o = &$this->objects[$id]; - } - switch ($action) { case 'new': // make the new object - $this->objects[$id] = array('t' => 'encryption', 'info' => $options); + $this->objects[$id] = ['t' => 'encryption', 'info' => $options]; $this->arc4_objnum = $id; + break; - // figure out the additional paramaters required + case 'keys': + // figure out the additional parameters required $pad = chr(0x28) . chr(0xBF) . chr(0x4E) . chr(0x5E) . chr(0x4E) . chr(0x75) . chr(0x8A) . chr(0x41) . chr(0x64) . chr(0x00) . chr(0x4E) . chr(0x56) . chr(0xFF) . chr(0xFA) . chr(0x01) . chr(0x08) . chr(0x2E) . chr(0x2E) . chr(0x00) . chr(0xB6) . chr(0xD0) . chr(0x68) . chr(0x3E) . chr(0x80) . chr(0x2F) . chr(0x0C) . chr(0xA9) . chr(0xFE) . chr(0x64) . chr(0x53) . chr(0x69) . chr(0x7A); - $len = mb_strlen($options['owner'], '8bit'); + $info = $this->objects[$id]['info']; + + $len = mb_strlen($info['owner'], '8bit'); if ($len > 32) { - $owner = substr($options['owner'], 0, 32); + $owner = substr($info['owner'], 0, 32); } else { if ($len < 32) { - $owner = $options['owner'] . substr($pad, 0, 32 - $len); + $owner = $info['owner'] . substr($pad, 0, 32 - $len); } else { - $owner = $options['owner']; + $owner = $info['owner']; } } - $len = mb_strlen($options['user'], '8bit'); + $len = mb_strlen($info['user'], '8bit'); if ($len > 32) { - $user = substr($options['user'], 0, 32); + $user = substr($info['user'], 0, 32); } else { if ($len < 32) { - $user = $options['user'] . substr($pad, 0, 32 - $len); + $user = $info['user'] . substr($pad, 0, 32 - $len); } else { - $user = $options['user']; + $user = $info['user']; } } @@ -1796,7 +2793,7 @@ EOT; // now make the u value, phew. $tmp = $this->md5_16( - $user . $ovalue . chr($options['p']) . chr(255) . chr(255) . chr(255) . $this->fileIdentifier + $user . $ovalue . chr($info['p']) . chr(255) . chr(255) . chr(255) . hex2bin($this->fileIdentifier) ); $ukey = substr($tmp, 0, 5); @@ -1805,17 +2802,18 @@ EOT; $this->encrypted = true; $uvalue = $this->ARC4($pad); $this->objects[$id]['info']['U'] = $uvalue; - $this->encryptionKey = $ukey; // initialize the arc4 array break; case 'out': + $o = &$this->objects[$id]; + $res = "\n$id 0 obj\n<<"; $res .= "\n/Filter /Standard"; $res .= "\n/V 1"; $res .= "\n/R 2"; - $res .= "\n/O (" . $this->filterText($o['info']['O'], true, false) . ')'; - $res .= "\n/U (" . $this->filterText($o['info']['U'], true, false) . ')'; + $res .= "\n/O (" . $this->filterText($o['info']['O'], false, false) . ')'; + $res .= "\n/U (" . $this->filterText($o['info']['U'], false, false) . ')'; // and the p-value needs to be converted to account for the twos-complement approach $o['info']['p'] = (($o['info']['p'] ^ 255) + 1) * -1; $res .= "\n/P " . ($o['info']['p']); @@ -1823,6 +2821,158 @@ EOT; return $res; } + + return null; + } + + protected function o_indirect_references($id, $action, $options = null) + { + switch ($action) { + case 'new': + case 'add': + if ($id === 0) { + $id = ++$this->numObj; + $this->o_catalog($this->catalogId, 'names', $id); + $this->objects[$id] = ['t' => 'indirect_references', 'info' => $options]; + $this->indirectReferenceId = $id; + } else { + $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options); + } + break; + case 'out': + $res = "\n$id 0 obj << "; + + foreach ($this->objects[$id]['info'] as $referenceObjName => $referenceObjId) { + $res .= "/$referenceObjName $referenceObjId 0 R "; + } + + $res .= ">> endobj"; + return $res; + } + + return null; + } + + protected function o_names($id, $action, $options = null) + { + switch ($action) { + case 'new': + case 'add': + if ($id === 0) { + $id = ++$this->numObj; + $this->objects[$id] = ['t' => 'names', 'info' => [$options]]; + $this->o_indirect_references($this->indirectReferenceId, 'add', ['EmbeddedFiles' => $id]); + $this->embeddedFilesId = $id; + } else { + $this->objects[$id]['info'][] = $options; + } + break; + case 'out': + $info = &$this->objects[$id]['info']; + $res = ''; + if (count($info) > 0) { + $res = "\n$id 0 obj << /Names [ "; + + if ($this->encrypted) { + $this->encryptInit($id); + } + + foreach ($info as $entry) { + if ($this->encrypted) { + $filename = $this->ARC4($entry['filename']); + } else { + $filename = $entry['filename']; + } + + $res .= "($filename) " . $entry['dict_reference'] . " 0 R "; + } + + $res .= "] >> endobj"; + } + return $res; + } + + return null; + } + + protected function o_embedded_file_dictionary($id, $action, $options = null) + { + switch ($action) { + case 'new': + $embeddedFileId = ++$this->numObj; + $options['embedded_reference'] = $embeddedFileId; + $this->objects[$id] = ['t' => 'embedded_file_dictionary', 'info' => $options]; + $this->o_embedded_file($embeddedFileId, 'new', $options); + $options['dict_reference'] = $id; + $this->o_names($this->embeddedFilesId, 'add', $options); + break; + case 'out': + $info = &$this->objects[$id]['info']; + $filename = $this->utf8toUtf16BE($info['filename']); + $description = $this->utf8toUtf16BE($info['description']); + + if ($this->encrypted) { + $this->encryptInit($id); + $filename = $this->ARC4($filename); + $description = $this->ARC4($description); + } + + $filename = $this->filterText($filename, false, false); + $description = $this->filterText($description, false, false); + + $res = "\n$id 0 obj <>"; + $res .= " /F ($filename) /UF ($filename) /Desc ($description)"; + $res .= " >> endobj"; + return $res; + } + + return null; + } + + protected function o_embedded_file($id, $action, $options = null): ?string + { + switch ($action) { + case 'new': + $this->objects[$id] = ['t' => 'embedded_file', 'info' => $options]; + break; + case 'out': + $info = &$this->objects[$id]['info']; + + if ($this->compressionReady) { + $filepath = $info['filepath']; + $checksum = md5_file($filepath); + $f = fopen($filepath, "rb"); + + $file_content_compressed = ''; + $deflateContext = deflate_init(ZLIB_ENCODING_DEFLATE, ['level' => 6]); + while (($block = fread($f, 8192))) { + $file_content_compressed .= deflate_add($deflateContext, $block, ZLIB_NO_FLUSH); + } + $file_content_compressed .= deflate_add($deflateContext, '', ZLIB_FINISH); + $file_size_uncompressed = ftell($f); + fclose($f); + } else { + $file_content = file_get_contents($info['filepath']); + $file_size_uncompressed = mb_strlen($file_content, '8bit'); + $checksum = md5($file_content); + } + + if ($this->encrypted) { + $this->encryptInit($id); + $checksum = $this->ARC4($checksum); + $file_content_compressed = $this->ARC4($file_content_compressed); + } + $file_size_compressed = mb_strlen($file_content_compressed, '8bit'); + + $res = "\n$id 0 obj <>" . + " /Type/EmbeddedFile /Filter/FlateDecode" . + " /Length $file_size_compressed >> stream\n$file_content_compressed\nendstream\nendobj"; + + return $res; + } + + return null; } /** @@ -1832,6 +2982,9 @@ EOT; /** * calculate the 16 byte version of the 128 bit md5 digest of the string + * + * @param $string + * @return string */ function md5_16($string) { @@ -1846,6 +2999,8 @@ EOT; /** * initialize the encryption for processing a particular object + * + * @param $id */ function encryptInit($id) { @@ -1854,15 +3009,20 @@ EOT; if (mb_strlen($hex, '8bit') < 6) { $hex = substr('000000', 0, 6 - mb_strlen($hex, '8bit')) . $hex; } - $tmp .= chr(hexdec(substr($hex, 4, 2))) . chr(hexdec(substr($hex, 2, 2))) . chr( - hexdec(substr($hex, 0, 2)) - ) . chr(0) . chr(0); + $tmp .= chr(hexdec(substr($hex, 4, 2))) + . chr(hexdec(substr($hex, 2, 2))) + . chr(hexdec(substr($hex, 0, 2))) + . chr(0) + . chr(0) + ; $key = $this->md5_16($tmp); $this->ARC4_init(substr($key, 0, 10)); } /** * initialize the ARC4 encryption + * + * @param string $key */ function ARC4_init($key = '') { @@ -1895,6 +3055,9 @@ EOT; /** * ARC4 encrypt a text string + * + * @param $text + * @return string */ function ARC4($text) { @@ -1922,21 +3085,33 @@ EOT; /** * add a link in the document to an external URL + * + * @param $url + * @param $x0 + * @param $y0 + * @param $x1 + * @param $y1 */ function addLink($url, $x0, $y0, $x1, $y1) { $this->numObj++; - $info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1)); + $info = ['type' => 'link', 'url' => $url, 'rect' => [$x0, $y0, $x1, $y1]]; $this->o_annotation($this->numObj, 'new', $info); } /** * add a link in the document to an internal destination (ie. within the document) + * + * @param $label + * @param $x0 + * @param $y0 + * @param $x1 + * @param $y1 */ function addInternalLink($label, $x0, $y0, $x1, $y1) { $this->numObj++; - $info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1)); + $info = ['type' => 'ilink', 'label' => $label, 'rect' => [$x0, $y0, $x1, $y1]]; $this->o_annotation($this->numObj, 'new', $info); } @@ -1944,12 +3119,16 @@ EOT; * set the encryption of the document * can be used to turn it on and/or set the passwords which it will have. * also the functions that the user will have are set here, such as print, modify, add + * + * @param string $userPass + * @param string $ownerPass + * @param array $pc */ - function setEncryption($userPass = '', $ownerPass = '', $pc = array()) + function setEncryption($userPass = '', $ownerPass = '', $pc = []) { $p = bindec("11000000"); - $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32); + $options = ['print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32]; foreach ($pc as $k => $v) { if ($v && isset($options[$k])) { @@ -1969,7 +3148,7 @@ EOT; $ownerPass = $userPass; } - $this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p)); + $this->o_encryption($this->numObj, 'new', ['user' => $userPass, 'owner' => $ownerPass, 'p' => $p]); } } @@ -1982,6 +3161,9 @@ EOT; /** * return the pdf stream as a string returned from the function + * + * @param bool $debug + * @return string */ function output($debug = false) { @@ -1999,24 +3181,37 @@ EOT; $id = $this->catalogId; - $this->o_catalog($id, 'javascript', $js_id); + $this->o_indirect_references($this->indirectReferenceId, 'add', ['JavaScript' => $js_id]); + } + + if ($this->fileIdentifier === '') { + $tmp = implode('', $this->objects[$this->infoObject]['info']); + $this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand()); } if ($this->arc4_objnum) { + $this->o_encryption($this->arc4_objnum, 'keys'); $this->ARC4_init($this->encryptionKey); } $this->checkAllHere(); - $xref = array(); - $content = '%PDF-1.3'; + $xref = []; + $content = '%PDF-' . self::PDF_VERSION; $pos = mb_strlen($content, '8bit'); + // pre-process o_font objects before output of all objects + foreach ($this->objects as $k => $v) { + if ($v['t'] === 'font') { + $this->o_font($k, 'add'); + } + } + foreach ($this->objects as $k => $v) { $tmp = 'o_' . $v['t']; $cont = $this->$tmp($k, 'out'); $content .= $cont; - $xref[] = $pos; + $xref[] = $pos + 1; //+1 to account for \n at the start of each object $pos += mb_strlen($cont, '8bit'); } @@ -2026,34 +3221,45 @@ EOT; $content .= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n"; } - $content .= "trailer\n<<\n/Size " . (count($xref) + 1) . "\n/Root 1 0 R\n/Info $this->infoObject 0 R\n"; + $content .= "trailer\n<<\n" . + '/Size ' . (count($xref) + 1) . "\n" . + '/Root 1 0 R' . "\n" . + '/Info ' . $this->infoObject . " 0 R\n" + ; // if encryption has been applied to this document then add the marker for this dictionary if ($this->arc4_objnum > 0) { - $content .= "/Encrypt $this->arc4_objnum 0 R\n"; + $content .= '/Encrypt ' . $this->arc4_objnum . " 0 R\n"; } - if (mb_strlen($this->fileIdentifier, '8bit')) { - $content .= "/ID[<$this->fileIdentifier><$this->fileIdentifier>]\n"; - } + $content .= '/ID[<' . $this->fileIdentifier . '><' . $this->fileIdentifier . ">]\n"; // account for \n added at start of xref table $pos++; $content .= ">>\nstartxref\n$pos\n%%EOF\n"; + if (count($this->byteRange) > 0) { + foreach ($this->byteRange as $k => $v) { + $tmp = 'o_' . $v['t']; + $this->$tmp($k, 'byterange', ['content' => &$content]); + } + } + return $content; } /** - * intialize a new document + * initialize a new document * if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum * this function is called automatically by the constructor function + * + * @param array $pageSize */ - private function newDocument($pageSize = array(0, 0, 612, 792)) + private function newDocument($pageSize = [0, 0, 612, 792]) { $this->numObj = 0; - $this->objects = array(); + $this->objects = []; $this->numObj++; $this->o_catalog($this->numObj, 'new'); @@ -2087,19 +3293,14 @@ EOT; * note that if a php serialized version does not exist it will try and make one, but will * require write access to the directory to do it... it is MUCH faster to have these serialized * files. + * + * @param $font */ private function openFont($font) { // assume that $font contains the path and file but not the extension - $pos = strrpos($font, '/'); - - if ($pos === false) { - $dir = './'; - $name = $font; - } else { - $dir = substr($font, 0, $pos + 1); - $name = substr($font, $pos + 1); - } + $name = basename($font); + $dir = dirname($font); $fontcache = $this->fontcache; if ($fontcache == '') { @@ -2107,7 +3308,7 @@ EOT; } //$name filename without folder and extension of font metrics - //$dir folder of font metrics + //$dir folder of font metrics //$fontcache folder of runtime created php serialized version of font metrics. // If this is not given, the same folder as the font metrics will be used. // Storing and reusing serialized versions improves speed much @@ -2120,39 +3321,26 @@ EOT; $metrics_name = "$name.ufm"; } - $cache_name = "$metrics_name.php"; + $cache_name = "$metrics_name.json"; $this->addMessage("metrics: $metrics_name, cache: $cache_name"); - - if (file_exists($fontcache . $cache_name)) { - $this->addMessage("openFont: php file exists $fontcache$cache_name"); - $this->fonts[$font] = require($fontcache . $cache_name); - - if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) { - // if the font file is old, then clear it out and prepare for re-creation - $this->addMessage('openFont: clear out, make way for new version.'); - $this->fonts[$font] = null; - unset($this->fonts[$font]); - } - } else { - $old_cache_name = "php_$metrics_name"; - if (file_exists($fontcache . $old_cache_name)) { - $this->addMessage( - "openFont: php file doesn't exist $fontcache$cache_name, creating it from the old format" - ); - $old_cache = file_get_contents($fontcache . $old_cache_name); - file_put_contents($fontcache . $cache_name, 'openFont($font); + + if (file_exists($fontcache . '/' . $cache_name)) { + $this->addMessage("openFont: json metrics file exists $fontcache/$cache_name"); + $cached_font_info = json_decode(file_get_contents($fontcache . '/' . $cache_name), true); + if (!isset($cached_font_info['_version_']) || $cached_font_info['_version_'] != $this->fontcacheVersion) { + $this->addMessage('openFont: font cache is out of date, regenerating'); + } else { + $this->fonts[$font] = $cached_font_info; } } - if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) { + if (!isset($this->fonts[$font]) && file_exists("$dir/$metrics_name")) { // then rebuild the php_.afm file from the .afm file - $this->addMessage("openFont: build php file from $dir$metrics_name"); - $data = array(); + $this->addMessage("openFont: build php file from $dir/$metrics_name"); + $data = []; // 20 => 'space' - $data['codeToName'] = array(); + $data['codeToName'] = []; // Since we're not going to enable Unicode for the core fonts we need to use a font-based // setting for Unicode support rather than a global setting. @@ -2163,7 +3351,7 @@ EOT; $cidtogid = str_pad('', 256 * 256 * 2, "\x00"); } - $file = file($dir . $metrics_name); + $file = file("$dir/$metrics_name"); foreach ($file as $rowA) { $row = trim($rowA); @@ -2203,7 +3391,7 @@ EOT; //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; case 'C': // Found in AFM files $bits = explode(';', trim($row)); - $dtmp = array(); + $dtmp = ['C' => null, 'N' => null, 'WX' => null, 'B' => []]; foreach ($bits as $bit) { $bits2 = explode(' ', trim($bit)); @@ -2212,7 +3400,7 @@ EOT; } if (count($bits2) > 2) { - $dtmp[$bits2[0]] = array(); + $dtmp[$bits2[0]] = []; for ($i = 1; $i < count($bits2); $i++) { $dtmp[$bits2[0]][] = $bits2[$i]; } @@ -2228,15 +3416,15 @@ EOT; $width = floatval($dtmp['WX']); if ($c >= 0) { - if ($c != hexdec($n)) { + if (!ctype_xdigit($n) || $c != hexdec($n)) { $data['codeToName'][$c] = $n; } $data['C'][$c] = $width; - } else { + } elseif (isset($n)) { $data['C'][$n] = $width; } - if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') { + if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') { $data['MissingWidth'] = $width; } @@ -2249,7 +3437,7 @@ EOT; } $bits = explode(';', trim($row)); - $dtmp = array(); + $dtmp = ['G' => null, 'N' => null, 'U' => null, 'WX' => null]; foreach ($bits as $bit) { $bits2 = explode(' ', trim($bit)); @@ -2258,7 +3446,7 @@ EOT; } if (count($bits2) > 2) { - $dtmp[$bits2[0]] = array(); + $dtmp[$bits2[0]] = []; for ($i = 1; $i < count($bits2); $i++) { $dtmp[$bits2[0]][] = $bits2[$i]; } @@ -2281,15 +3469,15 @@ EOT; $cidtogid[$c * 2 + 1] = chr($glyph & 0xFF); } - if ($c != hexdec($n)) { + if (!ctype_xdigit($n) || $c != hexdec($n)) { $data['codeToName'][$c] = $n; } $data['C'][$c] = $width; - } else { + } elseif (isset($n)) { $data['C'][$n] = $width; } - if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') { + if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') { $data['MissingWidth'] = $width; } @@ -2298,9 +3486,9 @@ EOT; case 'KPX': break; // don't include them as they are not used yet //KPX Adieresis yacute -40 - $bits = explode(' ', trim($row)); + /*$bits = explode(' ', trim($row)); $data['KPX'][$bits[1]][$bits[2]] = $bits[3]; - break; + break;*/ } } } @@ -2316,8 +3504,8 @@ EOT; //Because of potential trouble with php safe mode, expect that the folder already exists. //If not existing, this will hit performance because of missing cached results. - if (is_dir(substr($fontcache, 0, -1)) && is_writable(substr($fontcache, 0, -1))) { - file_put_contents($fontcache . $cache_name, 'fonts[$font])) { $this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?"); } - - //pre_r($this->messages); } /** @@ -2336,9 +3522,19 @@ EOT; * note that encoding='none' will need to be used for symbolic fonts * and 'differences' => an array of mappings between numbers 0->255 and character names. * + * @param string $fontName + * @param string $encoding + * @param bool $set + * @param bool $isSubsetting + * @return int + * @throws FontNotFoundException */ - function selectFont($fontName, $encoding = '', $set = true) + function selectFont($fontName, $encoding = '', $set = true, $isSubsetting = true) { + if ($fontName === null || $fontName === '') { + return $this->currentFontNum; + } + $ext = substr($fontName, -4); if ($ext === '.afm' || $ext === '.ufm') { $fontName = substr($fontName, 0, mb_strlen($fontName) - 4); @@ -2356,11 +3552,8 @@ EOT; $font = &$this->fonts[$fontName]; - //$this->numFonts = md5($fontName); - $pos = strrpos($fontName, '/'); - // $dir = substr($fontName,0,$pos+1); - $name = substr($fontName, $pos + 1); - $options = array('name' => $name, 'fontFileName' => $fontName); + $name = basename($fontName); + $options = ['name' => $name, 'fontFileName' => $fontName, 'isSubsetting' => $isSubsetting]; if (is_array($encoding)) { // then encoding and differences might be set @@ -2378,287 +3571,24 @@ EOT; } } - $fontObj = $this->numObj; $this->o_font($this->numObj, 'new', $options); + + if (file_exists("$fontName.ttf")) { + $fileSuffix = 'ttf'; + } elseif (file_exists("$fontName.TTF")) { + $fileSuffix = 'TTF'; + } elseif (file_exists("$fontName.pfb")) { + $fileSuffix = 'pfb'; + } elseif (file_exists("$fontName.PFB")) { + $fileSuffix = 'PFB'; + } else { + $fileSuffix = ''; + } + + $font['fileSuffix'] = $fileSuffix; + $font['fontNum'] = $this->numFonts; - - // if this is a '.afm' font, and there is a '.pfa' file to go with it ( as there - // should be for all non-basic fonts), then load it into an object and put the - // references into the font object - $basefile = $fontName; - - $fbtype = ''; - if (file_exists("$basefile.pfb")) { - $fbtype = 'pfb'; - } else { - if (file_exists("$basefile.ttf")) { - $fbtype = 'ttf'; - } - } - - $fbfile = "$basefile.$fbtype"; - - // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb'; - // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf'; - $this->addMessage('selectFont: checking for - ' . $fbfile); - - // OAR - I don't understand this old check - // if (substr($fontName, -4) === '.afm' && strlen($fbtype)) { - if ($fbtype) { - $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName']; - // $fontObj = $this->numObj; - $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName"); - - // find the array of font widths, and put that into an object. - $firstChar = -1; - $lastChar = 0; - $widths = array(); - $cid_widths = array(); - - foreach ($font['C'] as $num => $d) { - if (intval($num) > 0 || $num == '0') { - if (!$font['isUnicode']) { - // With Unicode, widths array isn't used - if ($lastChar > 0 && $num > $lastChar + 1) { - for ($i = $lastChar + 1; $i < $num; $i++) { - $widths[] = 0; - } - } - } - - $widths[] = $d; - - if ($font['isUnicode']) { - $cid_widths[$num] = $d; - } - - if ($firstChar == -1) { - $firstChar = $num; - } - - $lastChar = $num; - } - } - - // also need to adjust the widths for the differences array - if (isset($options['differences'])) { - foreach ($options['differences'] as $charNum => $charName) { - if ($charNum > $lastChar) { - if (!$font['isUnicode']) { - // With Unicode, widths array isn't used - for ($i = $lastChar + 1; $i <= $charNum; $i++) { - $widths[] = 0; - } - } - - $lastChar = $charNum; - } - - if (isset($font['C'][$charName])) { - $widths[$charNum - $firstChar] = $font['C'][$charName]; - if ($font['isUnicode']) { - $cid_widths[$charName] = $font['C'][$charName]; - } - } - } - } - - if ($font['isUnicode']) { - $font['CIDWidths'] = $cid_widths; - } - - $this->addMessage('selectFont: FirstChar = ' . $firstChar); - $this->addMessage('selectFont: LastChar = ' . $lastChar); - - $widthid = -1; - - if (!$font['isUnicode']) { - // With Unicode, widths array isn't used - - $this->numObj++; - $this->o_contents($this->numObj, 'new', 'raw'); - $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']'; - $widthid = $this->numObj; - } - - $missing_width = 500; - $stemV = 70; - - if (isset($font['MissingWidth'])) { - $missing_width = $font['MissingWidth']; - } - if (isset($font['StdVW'])) { - $stemV = $font['StdVW']; - } else { - if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) { - $stemV = 120; - } - } - - // load the pfb file, and put that into an object too. - // note that pdf supports only binary format type 1 font files, though there is a - // simple utility to convert them from pfa to pfb. - // FIXME: should we move font subset creation to CPDF::output? See notes in issue #750. - if (!$this->isUnicode || $fbtype !== 'ttf' || empty($this->stringSubsets)) { - $data = file_get_contents($fbfile); - } else { - $this->stringSubsets[$fontName][] = 32; // Force space if not in yet - - $subset = $this->stringSubsets[$fontName]; - sort($subset); - - // Load font - $font_obj = Font::load($fbfile); - $font_obj->parse(); - - // Define subset - $font_obj->setSubset($subset); - $font_obj->reduce(); - - // Write new font - $tmp_name = "$fbfile.tmp." . uniqid(); - $font_obj->open($tmp_name, Font_Binary_Stream::modeWrite); - $font_obj->encode(array("OS/2")); - $font_obj->close(); - - // Parse the new font to get cid2gid and widths - $font_obj = Font::load($tmp_name); - - // Find Unicode char map table - $subtable = null; - foreach ($font_obj->getData("cmap", "subtables") as $_subtable) { - if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) { - $subtable = $_subtable; - break; - } - } - - if ($subtable) { - $glyphIndexArray = $subtable["glyphIndexArray"]; - $hmtx = $font_obj->getData("hmtx"); - - unset($glyphIndexArray[0xFFFF]); - - $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00"); - $font['CIDWidths'] = array(); - foreach ($glyphIndexArray as $cid => $gid) { - if ($cid >= 0 && $cid < 0xFFFF && $gid) { - $cidtogid[$cid * 2] = chr($gid >> 8); - $cidtogid[$cid * 2 + 1] = chr($gid & 0xFF); - } - - $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]); - $font['CIDWidths'][$cid] = $width; - } - - $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid)); - $font['CIDtoGID_Compressed'] = true; - - $data = file_get_contents($tmp_name); - } else { - $data = file_get_contents($fbfile); - } - - $font_obj->close(); - unlink($tmp_name); - } - - // create the font descriptor - $this->numObj++; - $fontDescriptorId = $this->numObj; - - $this->numObj++; - $pfbid = $this->numObj; - - // determine flags (more than a little flakey, hopefully will not matter much) - $flags = 0; - - if ($font['ItalicAngle'] != 0) { - $flags += pow(2, 6); - } - - if ($font['IsFixedPitch'] === 'true') { - $flags += 1; - } - - $flags += pow(2, 5); // assume non-sybolic - $list = array( - 'Ascent' => 'Ascender', - 'CapHeight' => 'CapHeight', - 'MissingWidth' => 'MissingWidth', - 'Descent' => 'Descender', - 'FontBBox' => 'FontBBox', - 'ItalicAngle' => 'ItalicAngle' - ); - $fdopt = array( - 'Flags' => $flags, - 'FontName' => $adobeFontName, - 'StemV' => $stemV - ); - - foreach ($list as $k => $v) { - if (isset($font[$v])) { - $fdopt[$k] = $font[$v]; - } - } - - if ($fbtype === 'pfb') { - $fdopt['FontFile'] = $pfbid; - } else { - if ($fbtype === 'ttf') { - $fdopt['FontFile2'] = $pfbid; - } - } - - $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt); - - // embed the font program - $this->o_contents($this->numObj, 'new'); - $this->objects[$pfbid]['c'] .= $data; - - // determine the cruicial lengths within this file - if ($fbtype === 'pfb') { - $l1 = strpos($data, 'eexec') + 6; - $l2 = strpos($data, '00000000') - $l1; - $l3 = mb_strlen($data, '8bit') - $l2 - $l1; - $this->o_contents( - $this->numObj, - 'add', - array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3) - ); - } else { - if ($fbtype == 'ttf') { - $l1 = mb_strlen($data, '8bit'); - $this->o_contents($this->numObj, 'add', array('Length1' => $l1)); - } - } - - // tell the font object about all this new stuff - $tmp = array( - 'BaseFont' => $adobeFontName, - 'MissingWidth' => $missing_width, - 'Widths' => $widthid, - 'FirstChar' => $firstChar, - 'LastChar' => $lastChar, - 'FontDescriptor' => $fontDescriptorId - ); - - if ($fbtype === 'ttf') { - $tmp['SubType'] = 'TrueType'; - } - - $this->addMessage("adding extra info to font.($fontObj)"); - - foreach ($tmp as $fk => $fv) { - $this->addMessage("$fk : $fv"); - } - - $this->o_font($fontObj, 'add', $tmp); - } else { - $this->addMessage( - 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts' - ); - } + $font['isSubsetting'] = $isSubsetting && $font['isUnicode'] && strtolower($fileSuffix) === 'ttf'; // also set the differences here, note that this means that these will take effect only the //first time that a font is selected, else they are ignored @@ -2676,12 +3606,9 @@ EOT; // applied to it as well. $this->currentFont = $this->currentBaseFont; $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; - - //$this->setCurrentFont(); } return $this->currentFontNum; - //return $this->numObj; } /** @@ -2689,7 +3616,7 @@ EOT; * note that this system is quite flexible, a bold-italic font can be completely different to a * italic-bold font, and even bold-bold will have to be defined within the family to have meaning * This function is to be called whenever the currentTextState is changed, it will update - * the currentFont setting to whatever the appropriatte family one is. + * the currentFont setting to whatever the appropriate family one is. * If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont * This function will change the currentFont to whatever it should be, but will not change the * currentBaseFont. @@ -2722,6 +3649,8 @@ EOT; /** * function for the user to find out what the ID is of the first page that was created during * startup - useful if they wish to add something to it later. + * + * @return int */ function getFirstPageId() { @@ -2730,6 +3659,8 @@ EOT; /** * add content to the currently active object + * + * @param $content */ private function addContent($content) { @@ -2738,32 +3669,35 @@ EOT; /** * sets the color for fill operations + * + * @param array $color + * @param bool $force */ function setColor($color, $force = false) { - $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null); + $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null]; if (!$force && $this->currentColor == $new_color) { return; } if (isset($new_color[3])) { - //$this->currentColor = $new_color; + $this->currentColor = $new_color; $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColor)); } else { if (isset($new_color[2])) { - //$this->currentColor = $new_color; - $this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $new_color)); + $this->currentColor = $new_color; + $this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColor)); } } } /** - * sets the color for fill operations + * @param string $fillRule */ function setFillRule($fillRule) { - if (!in_array($fillRule, array("nonzero", "evenodd"))) { + if (!in_array($fillRule, ["nonzero", "evenodd"])) { return; } @@ -2772,36 +3706,44 @@ EOT; /** * sets the color for stroke operations + * + * @param array $color + * @param bool $force */ function setStrokeColor($color, $force = false) { - $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null); + $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null]; if (!$force && $this->currentStrokeColor == $new_color) { return; } if (isset($new_color[3])) { - //$this->currentStrokeColor = $new_color; + $this->currentStrokeColor = $new_color; $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColor)); } else { if (isset($new_color[2])) { - //$this->currentStrokeColor = $new_color; - $this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $new_color)); + $this->currentStrokeColor = $new_color; + $this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColor)); } } } /** * Set the graphics state for compositions + * + * @param $parameters */ function setGraphicsState($parameters) { - // Create a new graphics state object - // FIXME: should actually keep track of states that have already been created... - $this->numObj++; - $this->o_extGState($this->numObj, 'new', $parameters); - $this->addContent("\n/GS$this->numStates gs"); + // Create a new graphics state object if necessary + if (($gstate = array_search($parameters, $this->gstates)) === false) { + $this->numObj++; + $this->o_extGState($this->numObj, 'new', $parameters); + $gstate = $this->numStates; + $this->gstates[$gstate] = $parameters; + } + $this->addContent("\n/GS$gstate gs"); } /** @@ -2818,7 +3760,7 @@ EOT; */ function setLineTransparency($mode, $opacity) { - static $blend_modes = array( + static $blend_modes = [ "Normal", "Multiply", "Screen", @@ -2831,26 +3773,30 @@ EOT; "SoftLight", "Difference", "Exclusion" - ); + ]; if (!in_array($mode, $blend_modes)) { $mode = "Normal"; } - // Only create a new graphics state if required - if ($mode === $this->currentLineTransparency["mode"] && - $opacity == $this->currentLineTransparency["opacity"] - ) { + if (is_null($this->currentLineTransparency)) { + $this->currentLineTransparency = []; + } + + if ($mode === (key_exists('mode', $this->currentLineTransparency) ? + $this->currentLineTransparency['mode'] : '') && + $opacity === (key_exists('opacity', $this->currentLineTransparency) ? + $this->currentLineTransparency["opacity"] : '')) { return; } $this->currentLineTransparency["mode"] = $mode; $this->currentLineTransparency["opacity"] = $opacity; - $options = array( + $options = [ "BM" => "/$mode", "CA" => (float)$opacity - ); + ]; $this->setGraphicsState($options); } @@ -2869,7 +3815,7 @@ EOT; */ function setFillTransparency($mode, $opacity) { - static $blend_modes = array( + static $blend_modes = [ "Normal", "Multiply", "Screen", @@ -2882,34 +3828,120 @@ EOT; "SoftLight", "Difference", "Exclusion" - ); + ]; if (!in_array($mode, $blend_modes)) { $mode = "Normal"; } - if ($mode === $this->currentFillTransparency["mode"] && - $opacity == $this->currentFillTransparency["opacity"] - ) { + if (is_null($this->currentFillTransparency)) { + $this->currentFillTransparency = []; + } + + if ($mode === (key_exists('mode', $this->currentFillTransparency) ? + $this->currentFillTransparency['mode'] : '') && + $opacity === (key_exists('opacity', $this->currentFillTransparency) ? + $this->currentFillTransparency["opacity"] : '')) { return; } $this->currentFillTransparency["mode"] = $mode; $this->currentFillTransparency["opacity"] = $opacity; - $options = array( + $options = [ "BM" => "/$mode", "ca" => (float)$opacity, - ); + ]; $this->setGraphicsState($options); } + /** + * draw a line from one set of coordinates to another + * + * @param float $x1 + * @param float $y1 + * @param float $x2 + * @param float $y2 + * @param bool $stroke + */ + function line($x1, $y1, $x2, $y2, $stroke = true) + { + $this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2)); + + if ($stroke) { + $this->addContent(' S'); + } + } + + /** + * draw a bezier curve based on 4 control points + * + * @param float $x0 + * @param float $y0 + * @param float $x1 + * @param float $y1 + * @param float $x2 + * @param float $y2 + * @param float $x3 + * @param float $y3 + */ + function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) + { + // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points + // as the control points for the curve. + $this->addContent( + sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) + ); + } + + /** + * draw a part of an ellipse + * + * @param float $x0 + * @param float $y0 + * @param float $astart + * @param float $afinish + * @param float $r1 + * @param float $r2 + * @param float $angle + * @param int $nSeg + */ + function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8) + { + $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false); + } + + /** + * draw a filled ellipse + * + * @param float $x0 + * @param float $y0 + * @param float $r1 + * @param float $r2 + * @param float $angle + * @param int $nSeg + * @param float $astart + * @param float $afinish + */ + function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360) + { + $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true); + } + + /** + * @param float $x + * @param float $y + */ function lineTo($x, $y) { $this->addContent(sprintf("\n%.3F %.3F l", $x, $y)); } + /** + * @param float $x + * @param float $y + */ function moveTo($x, $y) { $this->addContent(sprintf("\n%.3F %.3F m", $x, $y)); @@ -2917,6 +3949,13 @@ EOT; /** * draw a bezier curve based on 4 control points + * + * @param float $x1 + * @param float $y1 + * @param float $x2 + * @param float $y2 + * @param float $x3 + * @param float $y3 */ function curveTo($x1, $y1, $x2, $y2, $x3, $y3) { @@ -2925,6 +3964,11 @@ EOT; /** * draw a bezier curve based on 4 control points + * + * @param float $cpx + * @param float $cpy + * @param float $x + * @param float $y */ function quadTo($cpx, $cpy, $x, $y) { @@ -2951,6 +3995,19 @@ EOT; * from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse. * nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a * pretty crappy shape at 2, as we are approximating with bezier curves. + * + * @param float $x0 + * @param float $y0 + * @param float $r1 + * @param float $r2 + * @param float $angle + * @param int $nSeg + * @param float $astart + * @param float $afinish + * @param bool $close + * @param bool $fill + * @param bool $stroke + * @param bool $incomplete */ function ellipse( $x0, @@ -3063,6 +4120,12 @@ EOT; * (2) represents 2 on, 2 off, 2 on , 2 off ... * (2,1) is 2 on, 1 off, 2 on, 1 off.. etc * phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts. + * + * @param float $width + * @param string $cap + * @param string $join + * @param array $dash + * @param int $phase */ function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0) { @@ -3070,16 +4133,16 @@ EOT; $string = ''; if ($width > 0) { - $string .= sprintf("%.3F w", $width); + $string .= "$width w"; } - $ca = array('butt' => 0, 'round' => 1, 'square' => 2); + $ca = ['butt' => 0, 'round' => 1, 'square' => 2]; if (isset($ca[$cap])) { $string .= " $ca[$cap] J"; } - $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); + $ja = ['miter' => 0, 'round' => 1, 'bevel' => 2]; if (isset($ja[$join])) { $string .= " $ja[$join] j"; @@ -3093,6 +4156,65 @@ EOT; $this->addContent("\n$string"); } + /** + * draw a polygon, the syntax for this is similar to the GD polygon command + * + * @param float[] $p + * @param bool $fill + */ + public function polygon(array $p, bool $fill = false): void + { + $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1])); + + $n = count($p); + for ($i = 2; $i < $n; $i = $i + 2) { + $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1])); + } + + if ($fill) { + $this->addContent(' f'); + } else { + $this->addContent(' S'); + } + } + + /** + * a filled rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not + * the coordinates of the upper-right corner + * + * @param float $x1 + * @param float $y1 + * @param float $width + * @param float $height + */ + function filledRectangle($x1, $y1, $width, $height) + { + $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height)); + } + + /** + * draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not + * the coordinates of the upper-right corner + * + * @param float $x1 + * @param float $y1 + * @param float $width + * @param float $height + */ + function rectangle($x1, $y1, $width, $height) + { + $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height)); + } + + /** + * draw a rectangle, note that it is the width and height of the rectangle which are the secondary parameters, not + * the coordinates of the upper-right corner + * + * @param float $x1 + * @param float $y1 + * @param float $width + * @param float $height + */ function rect($x1, $y1, $width, $height) { $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height)); @@ -3105,12 +4227,195 @@ EOT; function fill() { - $this->addContent("\nf".($this->fillRule === "evenodd" ? "*" : "")); + $this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : "")); } function fillStroke() { - $this->addContent("\nb".($this->fillRule === "evenodd" ? "*" : "")); + $this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : "")); + } + + /** + * @param string $subtype + * @param integer $x + * @param integer $y + * @param integer $w + * @param integer $h + * @return int + */ + function addXObject($subtype, $x, $y, $w, $h) + { + $id = ++$this->numObj; + $this->o_xobject($id, 'new', ['Subtype' => $subtype, 'bbox' => [$x, $y, $w, $h]]); + return $id; + } + + /** + * @param integer $numXObject + * @param string $type + * @param array $options + */ + function setXObjectResource($numXObject, $type, $options) + { + if (in_array($type, ['procset', 'font', 'xObject'])) { + $this->o_xobject($numXObject, $type, $options); + } + } + + /** + * add signature + * + * $fieldSigId = $cpdf->addFormField(Cpdf::ACROFORM_FIELD_SIG, 'Signature1', 0, 0, 0, 0, 0); + * + * $signatureId = $cpdf->addSignature([ + * 'signcert' => file_get_contents('dompdf.crt'), + * 'privkey' => file_get_contents('dompdf.key'), + * 'password' => 'password', + * 'name' => 'DomPDF DEMO', + * 'location' => 'Home', + * 'reason' => 'First Form', + * 'contactinfo' => 'info' + * ]); + * $cpdf->setFormFieldValue($fieldSigId, "$signatureId 0 R"); + * + * @param string $signcert + * @param string $privkey + * @param string $password + * @param string|null $name + * @param string|null $location + * @param string|null $reason + * @param string|null $contactinfo + * @return int + */ + function addSignature($signcert, $privkey, $password = '', $name = null, $location = null, $reason = null, $contactinfo = null) { + $sigId = ++$this->numObj; + $this->o_sig($sigId, 'new', [ + 'SignCert' => $signcert, + 'PrivKey' => $privkey, + 'Password' => $password, + 'Name' => $name, + 'Location' => $location, + 'Reason' => $reason, + 'ContactInfo' => $contactinfo + ]); + + return $sigId; + } + + /** + * add field to form + * + * @param string $type ACROFORM_FIELD_* + * @param string $name + * @param $x0 + * @param $y0 + * @param $x1 + * @param $y1 + * @param integer $ff Field Flag ACROFORM_FIELD_*_* + * @param float $size + * @param array $color + * @return int + */ + public function addFormField($type, $name, $x0, $y0, $x1, $y1, $ff = 0, $size = 10.0, $color = [0, 0, 0]) + { + if (!$this->numFonts) { + $this->selectFont($this->defaultFont); + } + + $color = implode(' ', $color) . ' rg'; + + $currentFontNum = $this->currentFontNum; + $font = array_filter( + $this->objects[$this->currentNode]['info']['fonts'], + function ($item) use ($currentFontNum) { return $item['fontNum'] == $currentFontNum; } + ); + + $this->o_acroform($this->acroFormId, 'font', + ['objNum' => $font[0]['objNum'], 'fontNum' => $font[0]['fontNum']]); + + $fieldId = ++$this->numObj; + $this->o_field($fieldId, 'new', [ + 'rect' => [$x0, $y0, $x1, $y1], + 'F' => 4, + 'FT' => "/$type", + 'T' => $name, + 'Ff' => $ff, + 'pageid' => $this->currentPage, + 'da' => "$color /F$this->currentFontNum " . sprintf('%.1F Tf ', $size) + ]); + + return $fieldId; + } + + /** + * set Field value + * + * @param integer $numFieldObj + * @param string $value + */ + public function setFormFieldValue($numFieldObj, $value) + { + $this->o_field($numFieldObj, 'set', ['value' => $value]); + } + + /** + * set Field value (reference) + * + * @param integer $numFieldObj + * @param integer $numObj Object number + */ + public function setFormFieldRefValue($numFieldObj, $numObj) + { + $this->o_field($numFieldObj, 'set', ['refvalue' => $numObj]); + } + + /** + * set Field Appearanc (reference) + * + * @param integer $numFieldObj + * @param integer $normalNumObj + * @param integer|null $rolloverNumObj + * @param integer|null $downNumObj + */ + public function setFormFieldAppearance($numFieldObj, $normalNumObj, $rolloverNumObj = null, $downNumObj = null) + { + $appearance['N'] = $normalNumObj; + + if ($rolloverNumObj !== null) { + $appearance['R'] = $rolloverNumObj; + } + + if ($downNumObj !== null) { + $appearance['D'] = $downNumObj; + } + + $this->o_field($numFieldObj, 'set', ['appearance' => $appearance]); + } + + /** + * set Choice Field option values + * + * @param integer $numFieldObj + * @param array $value + */ + public function setFormFieldOpt($numFieldObj, $value) + { + $this->o_field($numFieldObj, 'set', ['options' => $value]); + } + + /** + * add form to document + * + * @param integer $sigFlags + * @param boolean $needAppearances + */ + public function addForm($sigFlags = 0, $needAppearances = false) + { + $this->acroFormId = ++$this->numObj; + $this->o_acroform($this->acroFormId, 'new', [ + 'NeedAppearances' => $needAppearances ? 'true' : 'false', + 'SigFlags' => $sigFlags + ]); } /** @@ -3118,6 +4423,9 @@ EOT; */ function save() { + // we must reset the color cache or it will keep bad colors after clipping + $this->currentColor = null; + $this->currentStrokeColor = null; $this->addContent("\nq"); } @@ -3126,26 +4434,123 @@ EOT; */ function restore() { + // we must reset the color cache or it will keep bad colors after clipping + $this->currentColor = null; + $this->currentStrokeColor = null; $this->addContent("\nQ"); } + /** + * draw a clipping rectangle, all the elements added after this will be clipped + * + * @param float $x1 + * @param float $y1 + * @param float $width + * @param float $height + */ + function clippingRectangle($x1, $y1, $width, $height) + { + $this->save(); + $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height)); + } + + /** + * draw a clipping rounded rectangle, all the elements added after this will be clipped + * + * @param float $x1 + * @param float $y1 + * @param float $w + * @param float $h + * @param float $rTL + * @param float $rTR + * @param float $rBR + * @param float $rBL + */ + function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) + { + $this->save(); + + // start: top edge, left end + $this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h)); + + // line: bottom edge, left end + $this->addContent(sprintf("\n%.3F %.3F l ", $x1, $y1 + $rBL)); + + // curve: bottom-left corner + $this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true); + + // line: right edge, bottom end + $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1)); + + // curve: bottom-right corner + $this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true); + + // line: right edge, top end + $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR)); + + // curve: bottom-right corner + $this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true); + + // line: bottom edge, right end + $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h)); + + // curve: top-right corner + $this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true); + + // line: top edge, left end + $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1)); + + // Close & clip + $this->addContent(" W n"); + } + + /** + * draw a clipping polygon, the syntax for this is similar to the GD polygon command + * + * @param float[] $p + */ + public function clippingPolygon(array $p): void + { + $this->save(); + + $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1])); + + $n = count($p); + for ($i = 2; $i < $n; $i = $i + 2) { + $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i + 1])); + } + + $this->addContent("W n"); + } + + /** + * ends the last clipping shape + */ + function clippingEnd() + { + $this->restore(); + } + /** * scale * * @param float $s_x scaling factor for width as percent * @param float $s_y scaling factor for height as percent - * @param float $x Origin abscisse + * @param float $x Origin abscissa * @param float $y Origin ordinate */ function scale($s_x, $s_y, $x, $y) { $y = $this->currentPageSize["height"] - $y; - $tm = array( - $s_x, 0, - 0, $s_y, - $x * (1 - $s_x), $y * (1 - $s_y) - ); + $tm = [ + $s_x, + 0, + 0, + $s_y, + $x * (1 - $s_x), + $y * (1 - $s_y) + ]; $this->transform($tm); } @@ -3158,11 +4563,14 @@ EOT; */ function translate($t_x, $t_y) { - $tm = array( - 1, 0, - 0, 1, - $t_x, -$t_y - ); + $tm = [ + 1, + 0, + 0, + 1, + $t_x, + -$t_y + ]; $this->transform($tm); } @@ -3171,7 +4579,7 @@ EOT; * rotate * * @param float $angle angle in degrees for counter-clockwise rotation - * @param float $x Origin abscisse + * @param float $x Origin abscissa * @param float $y Origin ordinate */ function rotate($angle, $x, $y) @@ -3182,11 +4590,14 @@ EOT; $cos_a = cos($a); $sin_a = sin($a); - $tm = array( - $cos_a, -$sin_a, - $sin_a, $cos_a, - $x - $sin_a * $y - $cos_a * $x, $y - $cos_a * $y + $sin_a * $x, - ); + $tm = [ + $cos_a, + -$sin_a, + $sin_a, + $cos_a, + $x - $sin_a * $y - $cos_a * $x, + $y - $cos_a * $y + $sin_a * $x, + ]; $this->transform($tm); } @@ -3196,7 +4607,7 @@ EOT; * * @param float $angle_x * @param float $angle_y - * @param float $x Origin abscisse + * @param float $x Origin abscissa * @param float $y Origin ordinate */ function skew($angle_x, $angle_y, $x, $y) @@ -3206,11 +4617,14 @@ EOT; $tan_x = tan(deg2rad($angle_x)); $tan_y = tan(deg2rad($angle_y)); - $tm = array( - 1, -$tan_y, - -$tan_x, 1, - $tan_x * $y, $tan_y * $x, - ); + $tm = [ + 1, + -$tan_y, + -$tan_x, + 1, + $tan_x * $y, + $tan_y * $x, + ]; $this->transform($tm); } @@ -3228,6 +4642,11 @@ EOT; /** * add a new page to the document * this also makes the new page the current active object + * + * @param int $insert + * @param int $id + * @param string $pos + * @return int */ function newPage($insert = 0, $id = 0, $pos = 'after') { @@ -3246,7 +4665,7 @@ EOT; // the id from the ezPdf class is the id of the contents of the page, not the page object itself // query that object to find the parent $rid = $this->objects[$id]['onPage']; - $opt = array('rid' => $rid, 'pos' => $pos); + $opt = ['rid' => $rid, 'pos' => $pos]; $this->o_page($this->numObj, 'new', $opt); } else { $this->o_page($this->numObj, 'new'); @@ -3278,58 +4697,40 @@ EOT; } /** - * output the pdf code, streaming it to the browser - * the relevant headers are set so that hopefully the browser will recognise it + * Streams the PDF to the client. + * + * @param string $filename The filename to present to the client. + * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). */ - function stream($options = '') + function stream($filename = "document.pdf", $options = []) { - // setting the options allows the adjustment of the headers - // values at the moment are: - // 'Content-Disposition' => 'filename' - sets the filename, though not too sure how well this will - // work as in my trial the browser seems to use the filename of the php file with .pdf on the end - // 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this header is not included, off by default - // this header seems to have caused some problems despite tha fact that it is supposed to solve - // them, so I am leaving it off by default. - // 'compress' = > 1 or 0 - apply content stream compression, this is on (1) by default - // 'Attachment' => 1 or 0 - if 1, force the browser to open a download dialog - if (!is_array($options)) { - $options = array(); - } - if (headers_sent()) { die("Unable to stream pdf: headers already sent"); } - $debug = empty($options['compression']); + if (!isset($options["compress"])) $options["compress"] = true; + if (!isset($options["Attachment"])) $options["Attachment"] = true; + + $debug = !$options['compress']; $tmp = ltrim($this->output($debug)); header("Cache-Control: private"); - header("Content-type: application/pdf"); - - //FIXME: I don't know that this is sufficient for determining content length (i.e. what about transport compression?) - header("Content-Length: " . mb_strlen($tmp, '8bit')); - $fileName = (isset($options['Content-Disposition']) ? $options['Content-Disposition'] : 'file.pdf'); - - if (!isset($options["Attachment"])) { - $options["Attachment"] = true; - } + header("Content-Type: application/pdf"); + header("Content-Length: " . mb_strlen($tmp, "8bit")); + $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf"; $attachment = $options["Attachment"] ? "attachment" : "inline"; - // detect the character encoding of the incoming file - $encoding = mb_detect_encoding($fileName); - $fallbackfilename = mb_convert_encoding($fileName, "ISO-8859-1", $encoding); - $encodedfallbackfilename = rawurlencode($fallbackfilename); - $encodedfilename = rawurlencode($fileName); + $encoding = mb_detect_encoding($filename); + $fallbackfilename = mb_convert_encoding($filename, "ISO-8859-1", $encoding); + $fallbackfilename = str_replace("\"", "", $fallbackfilename); + $encodedfilename = rawurlencode($filename); - header( - "Content-Disposition: $attachment; filename=" . $encodedfallbackfilename . "; filename*=UTF-8''$encodedfilename" - ); - - if (isset($options['Accept-Ranges']) && $options['Accept-Ranges'] == 1) { - //FIXME: Is this the correct value ... spec says 1#range-unit - header("Accept-Ranges: " . mb_strlen($tmp, '8bit')); + $contentDisposition = "Content-Disposition: $attachment; filename=\"$fallbackfilename\""; + if ($fallbackfilename !== $filename) { + $contentDisposition .= "; filename*=UTF-8''$encodedfilename"; } + header($contentDisposition); echo $tmp; flush(); @@ -3337,8 +4738,12 @@ EOT; /** * return the height in units of the current font in the given size + * + * @param float $size + * + * @return float */ - function getFontHeight($size) + public function getFontHeight(float $size): float { if (!$this->numFonts) { $this->selectFont($this->defaultFont); @@ -3369,7 +4774,12 @@ EOT; return $size * $h / 1000; } - function getFontXHeight($size) + /** + * @param float $size + * + * @return float + */ + public function getFontXHeight(float $size): float { if (!$this->numFonts) { $this->selectFont($this->defaultFont); @@ -3391,8 +4801,12 @@ EOT; * return the font descender, this will normally return a negative number * if you add this number to the baseline, you get the level of the bottom of the font * it is in the pdf user units + * + * @param float $size + * + * @return float */ - function getFontDescender($size) + public function getFontDescender(float $size): float { // note that this will most likely return a negative value if (!$this->numFonts) { @@ -3409,7 +4823,10 @@ EOT; * filter the text, this is applied to all text just before being inserted into the pdf document * it escapes the various things that need to be escaped, and so on * - * @access private + * @param $text + * @param bool $bom + * @param bool $convert_encoding + * @return string */ function filterText($text, $bom = true, $convert_encoding = true) { @@ -3420,21 +4837,132 @@ EOT; if ($convert_encoding) { $cf = $this->currentFont; if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) { - //$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); $text = $this->utf8toUtf16BE($text, $bom); } else { //$text = html_entity_decode($text, ENT_QUOTES); $text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8'); } + } elseif ($bom) { + $text = $this->utf8toUtf16BE($text, $bom); } // the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290) - return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); + return strtr($text, [')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r']); + } + + /** + * return array containing codepoints (UTF-8 character values) for the + * string passed in. + * + * based on the excellent TCPDF code by Nicola Asuni and the + * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html + * + * @param string $text UTF-8 string to process + * @return array UTF-8 codepoints array for the string + */ + function utf8toCodePointsArray(&$text) + { + $length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040 + $unicode = []; // array containing unicode values + $bytes = []; // array containing single character byte sequences + $numbytes = 1; // number of octets needed to represent the UTF-8 character + + for ($i = 0; $i < $length; $i++) { + $c = ord($text[$i]); // get one string character at time + if (count($bytes) === 0) { // get starting octect + if ($c <= 0x7F) { + $unicode[] = $c; // use the character "as is" because is ASCII + $numbytes = 1; + } elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN) + $bytes[] = ($c - 0xC0) << 0x06; + $numbytes = 2; + } elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN) + $bytes[] = ($c - 0xE0) << 0x0C; + $numbytes = 3; + } elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN) + $bytes[] = ($c - 0xF0) << 0x12; + $numbytes = 4; + } else { + // use replacement character for other invalid sequences + $unicode[] = 0xFFFD; + $bytes = []; + $numbytes = 1; + } + } elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN + $bytes[] = $c - 0x80; + if (count($bytes) === $numbytes) { + // compose UTF-8 bytes to a single unicode value + $c = $bytes[0]; + for ($j = 1; $j < $numbytes; $j++) { + $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); + } + if ((($c >= 0xD800) and ($c <= 0xDFFF)) or ($c >= 0x10FFFF)) { + // The definition of UTF-8 prohibits encoding character numbers between + // U+D800 and U+DFFF, which are reserved for use with the UTF-16 + // encoding form (as surrogate pairs) and do not directly represent + // characters. + $unicode[] = 0xFFFD; // use replacement character + } else { + $unicode[] = $c; // add char to array + } + // reset data for next char + $bytes = []; + $numbytes = 1; + } + } else { + // use replacement character for other invalid sequences + $unicode[] = 0xFFFD; + $bytes = []; + $numbytes = 1; + } + } + + return $unicode; + } + + /** + * convert UTF-8 to UTF-16 with an additional byte order marker + * at the front if required. + * + * based on the excellent TCPDF code by Nicola Asuni and the + * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html + * + * @param string $text UTF-8 string to process + * @param boolean $bom whether to add the byte order marker + * @return string UTF-16 result string + */ + function utf8toUtf16BE(&$text, $bom = true) + { + $out = $bom ? "\xFE\xFF" : ''; + + $unicode = $this->utf8toCodePointsArray($text); + foreach ($unicode as $c) { + if ($c === 0xFFFD) { + $out .= "\xFF\xFD"; // replacement character + } elseif ($c < 0x10000) { + $out .= chr($c >> 0x08) . chr($c & 0xFF); + } else { + $c -= 0x10000; + $w1 = 0xD800 | ($c >> 0x10); + $w2 = 0xDC00 | ($c & 0x3FF); + $out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF); + } + } + + return $out; } /** * given a start position and information about how text is to be laid out, calculate where * on the page the text will end + * + * @param $x + * @param $y + * @param $angle + * @param $size + * @param $wa + * @param $text + * @return array */ private function getTextPosition($x, $y, $angle, $size, $wa, $text) { @@ -3447,7 +4975,7 @@ EOT; $w += $wa * $nspaces; $a = deg2rad((float)$angle); - return array(cos($a) * $w + $x, -sin($a) * $w + $y); + return [cos($a) * $w + $x, -sin($a) * $w + $y]; } /** @@ -3473,7 +5001,10 @@ EOT; } /** - * add text to the document, at a specified location, size and angle on the page + * register text for font subsetting + * + * @param string $font + * @param string $text */ function registerText($font, $text) { @@ -3482,7 +5013,8 @@ EOT; } if (!isset($this->stringSubsets[$font])) { - $this->stringSubsets[$font] = array(); + $base_subset = "\u{fffd}\u{fffe}\u{ffff}"; + $this->stringSubsets[$font] = $this->utf8toCodePointsArray($base_subset); } $this->stringSubsets[$font] = array_unique( @@ -3492,6 +5024,15 @@ EOT; /** * add text to the document, at a specified location, size and angle on the page + * + * @param float $x + * @param float $y + * @param float $size + * @param string $text + * @param float $angle + * @param float $wordSpaceAdjust + * @param float $charSpaceAdjust + * @param bool $smallCaps */ function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false) { @@ -3499,25 +5040,25 @@ EOT; $this->selectFont($this->defaultFont); } - $text = str_replace(array("\r", "\n"), "", $text); + $text = str_replace(["\r", "\n"], "", $text); - if ($smallCaps) { - preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER); - $lower = $this->concatMatches($matches); - d($lower); + // if ($smallCaps) { + // preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER); + // $lower = $this->concatMatches($matches); + // d($lower); - preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER); - $other = $this->concatMatches($matches); - d($other); + // preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER); + // $other = $this->concatMatches($matches); + // d($other); - //$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text); - } + // $text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text); + // } // if there are any open callbacks, then they should be called, to show the start of the line if ($this->nCallback > 0) { for ($i = $this->nCallback; $i > 0; $i--) { // call each function - $info = array( + $info = [ 'x' => $x, 'y' => $y, 'angle' => $angle, @@ -3526,7 +5067,7 @@ EOT; 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'descender' => $this->callback[$i]['descender'] - ); + ]; $func = $this->callback[$i]['f']; $this->$func($info); @@ -3542,13 +5083,11 @@ EOT; ); } - if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust) { - $this->wordSpaceAdjust = $wordSpaceAdjust; + if ($wordSpaceAdjust != 0) { $this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust)); } - if ($charSpaceAdjust != 0 || $charSpaceAdjust != $this->charSpaceAdjust) { - $this->charSpaceAdjust = $charSpaceAdjust; + if ($charSpaceAdjust != 0) { $this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust)); } @@ -3559,16 +5098,22 @@ EOT; $part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start); $place_text = $this->filterText($part, false); // modify unicode text so that extra word spacing is manually implemented (bug #) - $cf = $this->currentFont; - if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) { + if ($this->fonts[$this->currentFont]['isUnicode'] && $wordSpaceAdjust != 0) { $space_scale = 1000 / $size; - //$place_text = str_replace(' ', ') ( ) '.($this->getTextWidth($size, chr(32), $wordSpaceAdjust)*-75).' (', $place_text); - $place_text = str_replace(' ', ' ) ' . (-round($space_scale * $wordSpaceAdjust)) . ' (', $place_text); + $place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text); } $this->addContent(" /F$this->currentFontNum " . sprintf('%.1F Tf ', $size)); $this->addContent(" [($place_text)] TJ"); } + if ($wordSpaceAdjust != 0) { + $this->addContent(sprintf(" %.3F Tw", 0)); + } + + if ($charSpaceAdjust != 0) { + $this->addContent(sprintf(" %.3F Tc", 0)); + } + $this->addContent(' ET'); // if there are any open callbacks, then they should be called, to show the end of the line @@ -3576,7 +5121,7 @@ EOT; for ($i = $this->nCallback; $i > 0; $i--) { // call each function $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text); - $info = array( + $info = [ 'x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, @@ -3585,20 +5130,31 @@ EOT; 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'descender' => $this->callback[$i]['descender'] - ); + ]; $func = $this->callback[$i]['f']; $this->$func($info); } } + + if ($this->fonts[$this->currentFont]['isSubsetting']) { + $this->registerText($this->currentFont, $text); + } } /** * calculate how wide a given text string will be on a page, at a given size. * this can be called externally, but is also used by the other class functions + * + * @param float $size + * @param string $text + * @param float $wordSpacing + * @param float $charSpacing + * + * @return float */ - function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0) + public function getTextWidth(float $size, string $text, float $wordSpacing = 0.0, float $charSpacing = 0.0): float { - static $ord_cache = array(); + static $ord_cache = []; // this function should not change any of the settings, though it will need to // track any directives which change during calculation, so copy them at the start @@ -3609,10 +5165,7 @@ EOT; $this->selectFont($this->defaultFont); } - $text = str_replace(array("\r", "\n"), "", $text); - - // converts a number or a float to a string so it can get the width - $text = "$text"; + $text = str_replace(["\r", "\n"], "", $text); // hmm, this is where it all starts to get tricky - use the font information to // calculate the width of each character, add them up and convert to user units @@ -3620,7 +5173,6 @@ EOT; $cf = $this->currentFont; $current_font = $this->fonts[$cf]; $space_scale = 1000 / ($size > 0 ? $size : 1); - $n_spaces = 0; if ($current_font['isUnicode']) { // for Unicode, use the code points array to calculate width rather @@ -3641,15 +5193,14 @@ EOT; // add additional padding for space if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space - $w += $word_spacing * $space_scale; - $n_spaces++; + $w += $wordSpacing * $space_scale; } } } - // add additionnal char spacing - if ($char_spacing != 0) { - $w += $char_spacing * $space_scale * (count($unicode) + $n_spaces); + // add additional char spacing + if ($charSpacing != 0) { + $w += $charSpacing * $space_scale * count($unicode); } } else { @@ -3677,15 +5228,14 @@ EOT; // add additional padding for space if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space - $w += $word_spacing * $space_scale; - $n_spaces++; + $w += $wordSpacing * $space_scale; } } } - // add additionnal char spacing - if ($char_spacing != 0) { - $w += $char_spacing * $space_scale * ($len + $n_spaces); + // add additional char spacing + if ($charSpacing != 0) { + $w += $charSpacing * $space_scale * $len; } } @@ -3700,6 +5250,7 @@ EOT; * end of the previous page, before the stack was closed down * This is to get around not being able to have open 'q' across pages * + * @param int $pageEnd */ function saveState($pageEnd = 0) { @@ -3715,11 +5266,11 @@ EOT; // $this->currentLineStyle = $opt['lin']; } else { $this->nStateStack++; - $this->stateStack[$this->nStateStack] = array( + $this->stateStack[$this->nStateStack] = [ 'col' => $this->currentColor, 'str' => $this->currentStrokeColor, 'lin' => $this->currentLineStyle - ); + ]; } $this->save(); @@ -3727,6 +5278,8 @@ EOT; /** * restore a previously saved state + * + * @param int $pageEnd */ function restoreState($pageEnd = 0) { @@ -3749,11 +5302,13 @@ EOT; * the current one. * this object will not appear until it is included within a page. * the function will return the object number + * + * @return int */ function openObject() { $this->nStack++; - $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage); + $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage]; // add a new object of the content type, to hold the data flow $this->numObj++; $this->o_contents($this->numObj, 'new'); @@ -3765,11 +5320,13 @@ EOT; /** * open an existing object for editing + * + * @param $id */ function reopenObject($id) { $this->nStack++; - $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage); + $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage]; $this->currentContents = $id; // also if this object is the primary contents for a page, then set the current page to its parent @@ -3796,11 +5353,13 @@ EOT; /** * stop an object from appearing on pages from this point on + * + * @param $id */ function stopObject($id) { // if an object has been appearing on pages up to now, then stop it, this page will - // be the last one that could contian it. + // be the last one that could contain it. if (isset($this->addLooseObjects[$id])) { $this->addLooseObjects[$id] = ''; } @@ -3808,6 +5367,9 @@ EOT; /** * after an object has been created, it wil only show if it has been added, using this function. + * + * @param $id + * @param string $options */ function addObject($id, $options = 'add') { @@ -3863,16 +5425,24 @@ EOT; /** * return a storable representation of a specific object + * + * @param $id + * @return string|null */ function serializeObject($id) { if (array_key_exists($id, $this->objects)) { return serialize($this->objects[$id]); } + + return null; } /** - * restore an object from its stored representation. returns its new object id. + * restore an object from its stored representation. Returns its new object id. + * + * @param $obj + * @return int */ function restoreSerializedObject($obj) { @@ -3884,9 +5454,33 @@ EOT; } /** - * add content to the documents info object + * Embeds a file inside the PDF + * + * @param string $filepath path to the file to store inside the PDF + * @param string $embeddedFilename the filename displayed in the list of embedded files + * @param string $description a description in the list of embedded files */ - function addInfo($label, $value = 0) + public function addEmbeddedFile(string $filepath, string $embeddedFilename, string $description): void + { + $this->numObj++; + $this->o_embedded_file_dictionary( + $this->numObj, + 'new', + [ + 'filepath' => $filepath, + 'filename' => $embeddedFilename, + 'description' => $description + ] + ); + } + + /** + * Add content to the documents info object + * + * @param string|array $label + * @param string $value + */ + public function addInfo($label, string $value = ""): void { // this will only work if the label is one of the valid ones. // modify this so that arrays can be passed as well. @@ -3894,7 +5488,7 @@ EOT; // else assume that they are both scalar, anything else will probably error if (is_array($label)) { foreach ($label as $l => $v) { - $this->o_info($this->infoObject, $l, $v); + $this->o_info($this->infoObject, $l, (string) $v); } } else { $this->o_info($this->infoObject, $label, $value); @@ -3903,21 +5497,29 @@ EOT; /** * set the viewer preferences of the document, it is up to the browser to obey these. + * + * @param $label + * @param int $value */ function setPreferences($label, $value = 0) { // this will only work if the label is one of the valid ones. if (is_array($label)) { foreach ($label as $l => $v) { - $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v)); + $this->o_catalog($this->catalogId, 'viewerPreferences', [$l => $v]); } } else { - $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value)); + $this->o_catalog($this->catalogId, 'viewerPreferences', [$label => $value]); } } /** * extract an integer from a position in a byte stream + * + * @param $data + * @param $pos + * @param $num + * @return int */ private function getBytes(&$data, $pos, $num) { @@ -3934,6 +5536,9 @@ EOT; /** * Check if image already added to pdf image directory. * If yes, need not to create again (pass empty data) + * + * @param string $imgname + * @return bool */ function image_iscached($imgname) { @@ -3944,19 +5549,20 @@ EOT; * add a PNG image into the document, from a GD object * this should work with remote files * - * @param string $file The PNG file - * @param float $x X position - * @param float $y Y position - * @param float $w Width - * @param float $h Height - * @param resource $img A GD resource - * @param bool $is_mask true if the image is a mask - * @param bool $mask true if the image is masked + * @param \GdImage|resource $img A GD resource + * @param string $file The PNG file + * @param float $x X position + * @param float $y Y position + * @param float $w Width + * @param float $h Height + * @param bool $is_mask true if the image is a mask + * @param bool $mask true if the image is masked + * @throws Exception */ - function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null) + function addImagePng(&$img, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null) { if (!function_exists("imagepng")) { - throw new Exception("The PHP GD extension is required, but is not installed."); + throw new \Exception("The PHP GD extension is required, but is not installed."); } //if already cached, need not to read again @@ -3978,6 +5584,11 @@ EOT; imagesavealpha($img, false/*!$is_mask && !$mask*/); $error = 0; + //DEBUG_IMG_TEMP + //debugpng + if (defined("DEBUGPNG") && DEBUGPNG) { + print '[addImagePng ' . $file . ']'; + } ob_start(); @imagepng($img); @@ -3986,6 +5597,11 @@ EOT; if ($data == '') { $error = 1; $errormsg = 'trouble writing file from GD'; + //DEBUG_IMG_TEMP + //debugpng + if (defined("DEBUGPNG") && DEBUGPNG) { + print 'trouble writing file from GD'; + } } if ($error) { @@ -3995,13 +5611,21 @@ EOT; } } //End isset($this->imagelist[$file]) (png Duplicate removal) - $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask); + $this->addPngFromBuf($data, $file, $x, $y, $w, $h, $is_mask, $mask); } + /** + * @param $file + * @param $x + * @param $y + * @param $w + * @param $h + * @param $byte + */ protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte) { // generate images - $img = imagecreatefrompng($file); + $img = @imagecreatefrompng($file); if ($img === false) { return; @@ -4016,12 +5640,12 @@ EOT; imagesavealpha($img, false); // create temp alpha file - $tempfile_alpha = tempnam($this->tmp, "cpdf_img_"); + $tempfile_alpha = @tempnam($this->tmp, "cpdf_img_"); @unlink($tempfile_alpha); $tempfile_alpha = "$tempfile_alpha.png"; // create temp plain file - $tempfile_plain = tempnam($this->tmp, "cpdf_img_"); + $tempfile_plain = @tempnam($this->tmp, "cpdf_img_"); @unlink($tempfile_plain); $tempfile_plain = "$tempfile_plain.png"; @@ -4035,71 +5659,87 @@ EOT; // Use PECL gmagick + Graphics Magic to process transparent PNG images if (extension_loaded("gmagick")) { - $gmagick = new Gmagick($file); + $gmagick = new \Gmagick($file); $gmagick->setimageformat('png'); // Get opacity channel (negative of alpha channel) $alpha_channel_neg = clone $gmagick; - $alpha_channel_neg->separateimagechannel(Gmagick::CHANNEL_OPACITY); + $alpha_channel_neg->separateimagechannel(\Gmagick::CHANNEL_OPACITY); // Negate opacity channel - $alpha_channel = new Gmagick(); + $alpha_channel = new \Gmagick(); $alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png"); - $alpha_channel->compositeimage($alpha_channel_neg, Gmagick::COMPOSITE_DIFFERENCE, 0, 0); - $alpha_channel->separateimagechannel(Gmagick::CHANNEL_RED); + $alpha_channel->compositeimage($alpha_channel_neg, \Gmagick::COMPOSITE_DIFFERENCE, 0, 0); + $alpha_channel->separateimagechannel(\Gmagick::CHANNEL_RED); $alpha_channel->writeimage($tempfile_alpha); // Cast to 8bit+palette - $imgalpha_ = imagecreatefrompng($tempfile_alpha); + $imgalpha_ = @imagecreatefrompng($tempfile_alpha); imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx); imagedestroy($imgalpha_); imagepng($imgalpha, $tempfile_alpha); // Make opaque image - $color_channels = new Gmagick(); + $color_channels = new \Gmagick(); $color_channels->newimage($wpx, $hpx, "#FFFFFF", "png"); - $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYRED, 0, 0); - $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYGREEN, 0, 0); - $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYBLUE, 0, 0); + $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYRED, 0, 0); + $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYGREEN, 0, 0); + $color_channels->compositeimage($gmagick, \Gmagick::COMPOSITE_COPYBLUE, 0, 0); $color_channels->writeimage($tempfile_plain); - $imgplain = imagecreatefrompng($tempfile_plain); - } // Use PECL imagick + ImageMagic to process transparent PNG images + $imgplain = @imagecreatefrompng($tempfile_plain); + } + // Use PECL imagick + ImageMagic to process transparent PNG images elseif (extension_loaded("imagick")) { // Native cloning was added to pecl-imagick in svn commit 263814 // the first version containing it was 3.0.1RC1 static $imagickClonable = null; if ($imagickClonable === null) { - $imagickClonable = version_compare(phpversion('imagick'), '3.0.1rc1') > 0; + $imagickClonable = true; + if (defined('Imagick::IMAGICK_EXTVER')) { + $imagickVersion = \Imagick::IMAGICK_EXTVER; + } else { + $imagickVersion = '0'; + } + if (version_compare($imagickVersion, '0.0.1', '>=')) { + $imagickClonable = version_compare($imagickVersion, '3.0.1rc1', '>='); + } } - $imagick = new Imagick($file); + $imagick = new \Imagick($file); $imagick->setFormat('png'); // Get opacity channel (negative of alpha channel) - $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone(); - $alpha_channel->separateImageChannel(Imagick::CHANNEL_ALPHA); - $alpha_channel->negateImage(true); - $alpha_channel->writeImage($tempfile_alpha); + if ($imagick->getImageAlphaChannel()) { + $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone(); + $alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA); + // Since ImageMagick7 negate invert transparency as default + if (\Imagick::getVersion()['versionNumber'] < 1800) { + $alpha_channel->negateImage(true); + } + $alpha_channel->writeImage($tempfile_alpha); - // Cast to 8bit+palette - $imgalpha_ = imagecreatefrompng($tempfile_alpha); - imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx); - imagedestroy($imgalpha_); - imagepng($imgalpha, $tempfile_alpha); + // Cast to 8bit+palette + $imgalpha_ = @imagecreatefrompng($tempfile_alpha); + imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx); + imagedestroy($imgalpha_); + imagepng($imgalpha, $tempfile_alpha); + } else { + $tempfile_alpha = null; + } // Make opaque image - $color_channels = new Imagick(); + $color_channels = new \Imagick(); $color_channels->newImage($wpx, $hpx, "#FFFFFF", "png"); - $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYRED, 0, 0); - $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYGREEN, 0, 0); - $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYBLUE, 0, 0); + $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYRED, 0, 0); + $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYGREEN, 0, 0); + $color_channels->compositeImage($imagick, \Imagick::COMPOSITE_COPYBLUE, 0, 0); $color_channels->writeImage($tempfile_plain); - $imgplain = imagecreatefrompng($tempfile_plain); + $imgplain = @imagecreatefrompng($tempfile_plain); } else { // allocated colors cache - $allocated_colors = array(); + $allocated_colors = []; // extract alpha channel for ($xpx = 0; $xpx < $wpx; ++$xpx) { @@ -4111,7 +5751,7 @@ EOT; if ($eight_bit) { // with gamma correction $gammacorr = 2.2; - $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255; + $pixel = round(pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255); } else { // without gamma correction $pixel = (127 - $alpha) * 2; @@ -4141,27 +5781,49 @@ EOT; imagepng($imgplain, $tempfile_plain); } + $this->imageAlphaList[$file] = [$tempfile_alpha, $tempfile_plain]; + // embed mask image - $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true); - imagedestroy($imgalpha); + if ($tempfile_alpha) { + $this->addImagePng($imgalpha, $tempfile_alpha, $x, $y, $w, $h, true); + imagedestroy($imgalpha); + $this->imageCache[] = $tempfile_alpha; + } // embed image, masked with previously embedded mask - $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, true); + $this->addImagePng($imgplain, $tempfile_plain, $x, $y, $w, $h, false, ($tempfile_alpha !== null)); imagedestroy($imgplain); - - // remove temp files - unlink($tempfile_alpha); - unlink($tempfile_plain); + $this->imageCache[] = $tempfile_plain; } /** * add a PNG image into the document, from a file * this should work with remote files + * + * @param $file + * @param $x + * @param $y + * @param int $w + * @param int $h + * @throws Exception */ function addPngFromFile($file, $x, $y, $w = 0, $h = 0) { if (!function_exists("imagecreatefrompng")) { - throw new Exception("The PHP GD extension is required, but is not installed."); + throw new \Exception("The PHP GD extension is required, but is not installed."); + } + + if (isset($this->imageAlphaList[$file])) { + [$alphaFile, $plainFile] = $this->imageAlphaList[$file]; + + if ($alphaFile) { + $img = null; + $this->addImagePng($img, $alphaFile, $x, $y, $w, $h, true); + } + + $img = null; + $this->addImagePng($img, $plainFile, $x, $y, $w, $h, false, ($plainFile !== null)); + return; } //if already cached, need not to read again @@ -4177,10 +5839,11 @@ EOT; // 3 => indexed // 4 => greyscale with alpha // 6 => fullcolor with alpha - $is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4); + $is_alpha = in_array($color_type, [4, 6]) || ($color_type == 3 && $bit_depth != 4); if ($is_alpha) { // exclude grayscale alpha - return $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type); + $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type); + return; } //png files typically contain an alpha channel. @@ -4216,7 +5879,7 @@ EOT; imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy); imagedestroy($imgtmp); } - $this->addImagePng($file, $x, $y, $w, $h, $img); + $this->addImagePng($img, $file, $x, $y, $w, $h); if ($img) { imagedestroy($img); @@ -4224,9 +5887,44 @@ EOT; } /** - * add a PNG image into the document, from a memory buffer of the file + * add a PNG image into the document, from a file + * this should work with remote files + * + * @param $file + * @param $x + * @param $y + * @param int $w + * @param int $h */ - function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null) + function addSvgFromFile($file, $x, $y, $w = 0, $h = 0) + { + $doc = new \Svg\Document(); + $doc->loadFile($file); + $dimensions = $doc->getDimensions(); + + $this->save(); + + $this->transform([$w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y]); + + $surface = new \Svg\Surface\SurfaceCpdf($doc, $this); + $doc->render($surface); + + $this->restore(); + } + + /** + * add a PNG image into the document, from a memory buffer of the file + * + * @param $data + * @param $file + * @param $x + * @param $y + * @param float $w + * @param float $h + * @param bool $is_mask + * @param null $mask + */ + function addPngFromBuf(&$data, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null) { if (isset($this->imagelist[$file])) { $data = null; @@ -4248,6 +5946,10 @@ EOT; if (mb_substr($data, 0, 8, '8bit') != $header) { $error = 1; + if (defined("DEBUGPNG") && DEBUGPNG) { + print '[addPngFromFile this file does not have a valid header ' . $file . ']'; + } + $errormsg = 'this file does not have a valid header'; } } @@ -4259,7 +5961,7 @@ EOT; // cycle through the file, identifying chunks $haveHeader = 0; - $info = array(); + $info = []; $idata = ''; $pdata = ''; @@ -4284,7 +5986,7 @@ EOT; $error = 1; //debugpng - if (DEBUGPNG) { + if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile unsupported compression method ' . $file . ']'; } @@ -4295,7 +5997,7 @@ EOT; $error = 1; //debugpng - if (DEBUGPNG) { + if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile unsupported filter method ' . $file . ']'; } @@ -4314,16 +6016,16 @@ EOT; case 'tRNS': //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk //print "tRNS found, color type = ".$info['colorType']."\n"; - $transparency = array(); + $transparency = []; switch ($info['colorType']) { // indexed color, rbg case 3: /* corresponding to entries in the plte chunk - Alpha for palette index 0: 1 byte - Alpha for palette index 1: 1 byte - ...etc... - */ + Alpha for palette index 0: 1 byte + Alpha for palette index 1: 1 byte + ...etc... + */ // there will be one entry for each palette entry. up until the last non-opaque entry. // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent) $transparency['type'] = 'indexed'; @@ -4341,8 +6043,8 @@ EOT; // grayscale case 0: /* corresponding to entries in the plte chunk - Gray: 2 bytes, range 0 .. (2^bitdepth)-1 - */ + Gray: 2 bytes, range 0 .. (2^bitdepth)-1 + */ // $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale $transparency['type'] = 'indexed'; $transparency['data'] = ord($data[$p + 8 + 1]); @@ -4351,10 +6053,10 @@ EOT; // truecolor case 2: /* corresponding to entries in the plte chunk - Red: 2 bytes, range 0 .. (2^bitdepth)-1 - Green: 2 bytes, range 0 .. (2^bitdepth)-1 - Blue: 2 bytes, range 0 .. (2^bitdepth)-1 - */ + Red: 2 bytes, range 0 .. (2^bitdepth)-1 + Green: 2 bytes, range 0 .. (2^bitdepth)-1 + Blue: 2 bytes, range 0 .. (2^bitdepth)-1 + */ $transparency['r'] = $this->getBytes($data, $p + 8, 2); // r from truecolor $transparency['g'] = $this->getBytes($data, $p + 10, 2); @@ -4367,7 +6069,7 @@ EOT; //unsupported transparency type default: - if (DEBUGPNG) { + if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile unsupported transparency type ' . $file . ']'; } break; @@ -4387,7 +6089,7 @@ EOT; $error = 1; //debugpng - if (DEBUGPNG) { + if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile information header is missing ' . $file . ']'; } @@ -4398,7 +6100,7 @@ EOT; $error = 1; //debugpng - if (DEBUGPNG) { + if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile no support for interlaced images in pdf ' . $file . ']'; } @@ -4410,7 +6112,7 @@ EOT; $error = 1; //debugpng - if (DEBUGPNG) { + if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile bit depth of 8 or less is supported ' . $file . ']'; } @@ -4438,11 +6140,11 @@ EOT; $error = 1; //debugpng - if (DEBUGPNG) { + if (defined("DEBUGPNG") && DEBUGPNG) { print '[addPngFromFile alpha channel not supported: ' . $info['colorType'] . ' ' . $file . ']'; } - $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.'; + $errormsg = 'transparency alpha channel not supported, transparency only supported for palette images.'; } } @@ -4460,7 +6162,7 @@ EOT; $this->numObj++; // $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width'])); - $options = array( + $options = [ 'label' => $label, 'data' => $idata, 'bitsPerComponent' => $info['bitDepth'], @@ -4472,14 +6174,14 @@ EOT; 'ncolor' => $ncolor, 'masked' => $mask, 'isMask' => $is_mask - ); + ]; if (isset($transparency)) { $options['transparency'] = $transparency; } $this->o_image($this->numObj, 'new', $options); - $this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']); + $this->imagelist[$file] = ['label' => $label, 'w' => $info['width'], 'h' => $info['height']]; } if ($is_mask) { @@ -4504,6 +6206,12 @@ EOT; /** * add a JPEG image into the document, from a file + * + * @param $img + * @param $x + * @param $y + * @param int $w + * @param int $h */ function addJpegFromFile($img, $x, $y, $w = 0, $h = 0) { @@ -4545,22 +6253,31 @@ EOT; $h = $w * $imageHeight / $imageWidth; } - $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img); + $this->addJpegImage_common($data, $img, $imageWidth, $imageHeight, $x, $y, $w, $h, $channels); } /** * common code used by the two JPEG adding functions + * @param $data + * @param $imgname + * @param $imageWidth + * @param $imageHeight + * @param $x + * @param $y + * @param int $w + * @param int $h + * @param int $channels */ private function addJpegImage_common( &$data, + $imgname, + $imageWidth, + $imageHeight, $x, $y, $w = 0, $h = 0, - $imageWidth, - $imageHeight, - $channels = 3, - $imgname + $channels = 3 ) { if ($this->image_iscached($imgname)) { $label = $this->imagelist[$imgname]['label']; @@ -4584,21 +6301,21 @@ EOT; $this->o_image( $this->numObj, 'new', - array( + [ 'label' => $label, 'data' => &$data, 'iw' => $imageWidth, 'ih' => $imageHeight, 'channels' => $channels - ) + ] ); - $this->imagelist[$imgname] = array( + $this->imagelist[$imgname] = [ 'label' => $label, 'w' => $imageWidth, 'h' => $imageHeight, 'c' => $channels - ); + ]; } $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label)); @@ -4606,11 +6323,16 @@ EOT; /** * specify where the document should open when it first starts + * + * @param $style + * @param int $a + * @param int $b + * @param int $c */ function openHere($style, $a = 0, $b = 0, $c = 0) { // this function will open the document at a specified page, in a specified style - // the values for style, and the required paramters are: + // the values for style, and the required parameters are: // 'XYZ' left, top, zoom // 'Fit' // 'FitH' top @@ -4623,7 +6345,7 @@ EOT; $this->o_destination( $this->numObj, 'new', - array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c) + ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c] ); $id = $this->catalogId; $this->o_catalog($id, 'openHere', $this->numObj); @@ -4633,8 +6355,6 @@ EOT; * Add JavaScript code to the PDF document * * @param string $code - * - * @return void */ function addJavascript($code) { @@ -4643,6 +6363,12 @@ EOT; /** * create a labelled destination within the document + * + * @param $label + * @param $style + * @param int $a + * @param int $b + * @param int $c */ function addDestination($label, $style, $a = 0, $b = 0, $c = 0) { @@ -4653,7 +6379,7 @@ EOT; $this->o_destination( $this->numObj, 'new', - array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c) + ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c] ); $id = $this->numObj; @@ -4665,6 +6391,9 @@ EOT; * define font families, this is used to initialize the font families for the default fonts * and for the user to add new ones for their fonts. The default bahavious can be overridden should * that be desired. + * + * @param $family + * @param string $options */ function setFontFamily($family, $options = '') { @@ -4674,28 +6403,28 @@ EOT; // these font families will be used to enable bold and italic markers to be included // within text streams. html forms will be used... $this->fontFamilies['Helvetica.afm'] = - array( + [ 'b' => 'Helvetica-Bold.afm', 'i' => 'Helvetica-Oblique.afm', 'bi' => 'Helvetica-BoldOblique.afm', 'ib' => 'Helvetica-BoldOblique.afm' - ); + ]; $this->fontFamilies['Courier.afm'] = - array( + [ 'b' => 'Courier-Bold.afm', 'i' => 'Courier-Oblique.afm', 'bi' => 'Courier-BoldOblique.afm', 'ib' => 'Courier-BoldOblique.afm' - ); + ]; $this->fontFamilies['Times-Roman.afm'] = - array( + [ 'b' => 'Times-Bold.afm', 'i' => 'Times-Italic.afm', 'bi' => 'Times-BoldItalic.afm', 'ib' => 'Times-BoldItalic.afm' - ); + ]; } } else { @@ -4709,6 +6438,8 @@ EOT; /** * used to add messages for use in debugging + * + * @param $message */ function addMessage($message) { @@ -4717,6 +6448,8 @@ EOT; /** * a few functions which should allow the document to be treated transactionally. + * + * @param $action */ function transaction($action) { diff --git a/library/vendor/dompdf/lib/fonts/Courier-Bold.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Bold.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Courier-Bold.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Bold.afm diff --git a/library/vendor/dompdf/lib/fonts/Courier-BoldOblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-BoldOblique.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Courier-BoldOblique.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-BoldOblique.afm diff --git a/library/vendor/dompdf/lib/fonts/Courier-Oblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Oblique.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Courier-Oblique.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier-Oblique.afm diff --git a/library/vendor/dompdf/lib/fonts/Courier.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Courier.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Courier.afm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Bold.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Bold.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-BoldOblique.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans-Oblique.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans-Oblique.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSans.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSans.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSans.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Bold.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-BoldOblique.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono-Oblique.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSansMono.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSansMono.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSansMono.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Bold.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Bold.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-BoldItalic.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif-Italic.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif-Italic.ufm diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif.ttf b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ttf similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif.ttf rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ttf diff --git a/library/vendor/dompdf/lib/fonts/DejaVuSerif.ufm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ufm similarity index 100% rename from library/vendor/dompdf/lib/fonts/DejaVuSerif.ufm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/DejaVuSerif.ufm diff --git a/library/vendor/dompdf/lib/fonts/Helvetica-Bold.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Bold.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Helvetica-Bold.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Bold.afm diff --git a/library/vendor/dompdf/lib/fonts/Helvetica-BoldOblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-BoldOblique.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Helvetica-BoldOblique.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-BoldOblique.afm diff --git a/library/vendor/dompdf/lib/fonts/Helvetica-Oblique.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Oblique.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Helvetica-Oblique.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica-Oblique.afm diff --git a/library/vendor/dompdf/lib/fonts/Helvetica.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Helvetica.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Helvetica.afm diff --git a/library/vendor/dompdf/lib/fonts/Symbol.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Symbol.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Symbol.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Symbol.afm diff --git a/library/vendor/dompdf/lib/fonts/Times-Bold.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Bold.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Times-Bold.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Bold.afm diff --git a/library/vendor/dompdf/lib/fonts/Times-BoldItalic.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-BoldItalic.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Times-BoldItalic.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-BoldItalic.afm diff --git a/library/vendor/dompdf/lib/fonts/Times-Italic.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Italic.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Times-Italic.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Italic.afm diff --git a/library/vendor/dompdf/lib/fonts/Times-Roman.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Roman.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/Times-Roman.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/Times-Roman.afm diff --git a/library/vendor/dompdf/lib/fonts/ZapfDingbats.afm b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/ZapfDingbats.afm similarity index 100% rename from library/vendor/dompdf/lib/fonts/ZapfDingbats.afm rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/ZapfDingbats.afm diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json new file mode 100644 index 000000000..c6abf158b --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/installed-fonts.dist.json @@ -0,0 +1,80 @@ +{ + "sans-serif": { + "normal": "Helvetica", + "bold": "Helvetica-Bold", + "italic": "Helvetica-Oblique", + "bold_italic": "Helvetica-BoldOblique" + }, + "times": { + "normal": "Times-Roman", + "bold": "Times-Bold", + "italic": "Times-Italic", + "bold_italic": "Times-BoldItalic" + }, + "times-roman": { + "normal": "Times-Roman", + "bold": "Times-Bold", + "italic": "Times-Italic", + "bold_italic": "Times-BoldItalic" + }, + "courier": { + "normal": "Courier", + "bold": "Courier-Bold", + "italic": "Courier-Oblique", + "bold_italic": "Courier-BoldOblique" + }, + "helvetica": { + "normal": "Helvetica", + "bold": "Helvetica-Bold", + "italic": "Helvetica-Oblique", + "bold_italic": "Helvetica-BoldOblique" + }, + "zapfdingbats": { + "normal": "ZapfDingbats", + "bold": "ZapfDingbats", + "italic": "ZapfDingbats", + "bold_italic": "ZapfDingbats" + }, + "symbol": { + "normal": "Symbol", + "bold": "Symbol", + "italic": "Symbol", + "bold_italic": "Symbol" + }, + "serif": { + "normal": "Times-Roman", + "bold": "Times-Bold", + "italic": "Times-Italic", + "bold_italic": "Times-BoldItalic" + }, + "monospace": { + "normal": "Courier", + "bold": "Courier-Bold", + "italic": "Courier-Oblique", + "bold_italic": "Courier-BoldOblique" + }, + "fixed": { + "normal": "Courier", + "bold": "Courier-Bold", + "italic": "Courier-Oblique", + "bold_italic": "Courier-BoldOblique" + }, + "dejavu sans": { + "bold": "DejaVuSans-Bold", + "bold_italic": "DejaVuSans-BoldOblique", + "italic": "DejaVuSans-Oblique", + "normal": "DejaVuSans" + }, + "dejavu sans mono": { + "bold": "DejaVuSansMono-Bold", + "bold_italic": "DejaVuSansMono-BoldOblique", + "italic": "DejaVuSansMono-Oblique", + "normal": "DejaVuSansMono" + }, + "dejavu serif": { + "bold": "DejaVuSerif-Bold", + "bold_italic": "DejaVuSerif-BoldItalic", + "italic": "DejaVuSerif-Italic", + "normal": "DejaVuSerif" + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/lib/fonts/mustRead.html b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/mustRead.html similarity index 100% rename from library/vendor/dompdf/lib/fonts/mustRead.html rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/fonts/mustRead.html diff --git a/library/vendor/dompdf/lib/res/broken_image.png b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.png similarity index 100% rename from library/vendor/dompdf/lib/res/broken_image.png rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.png diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.svg b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.svg new file mode 100644 index 000000000..83ba7e74c --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/broken_image.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/library/vendor/dompdf/lib/res/html.css b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/html.css similarity index 94% rename from library/vendor/dompdf/lib/res/html.css rename to library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/html.css index 372ff6e3c..89dcde633 100644 --- a/library/vendor/dompdf/lib/res/html.css +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/lib/res/html.css @@ -2,15 +2,12 @@ * dompdf default stylesheet. * * @package dompdf - * @link http://dompdf.github.com/ - * @author Benj Carson - * @author Blake Ross - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * * Portions from Mozilla * @link https://dxr.mozilla.org/mozilla-central/source/layout/style/res/html.css - * @license http://mozilla.org/MPL/2.0/ Mozilla Public License, v. 2.0 + * @license http://mozilla.org/MPL/2.0/ Mozilla Public License, v. 2.0 * * Portions from W3C * @link https://drafts.csswg.org/css-ui-3/#default-style-sheet @@ -48,7 +45,7 @@ summary { body { page-break-before: avoid; - display: block; + display: block !important; counter-increment: page; } @@ -148,16 +145,18 @@ table { } table[border] { - border-style: outset; - border-color: gray; + border: outset gray; } -/* This won't work (???) */ -/* table[border] td, table[border] th { - border: 1pt solid grey; -}*/ + border: 1px inset gray; +} + +table[border="0"] td, +table[border="0"] th { + border-width: 0; +} /* make sure backgrounds are inherited in tables -- see bug 4510 */ td, th, tr { @@ -213,14 +212,12 @@ td { th { display: table-cell; vertical-align: inherit; + text-align: center; font-weight: bold; padding: 1px; } /* inlines */ -q { - quotes: '"' '"' "'" "'"; /* FIXME only the first level is used */ -} q:before { content: open-quote; diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/CPDF.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/CPDF.php new file mode 100644 index 000000000..e8fc6ae4d --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/CPDF.php @@ -0,0 +1,944 @@ + [0.0, 0.0, 4767.87, 6740.79], + "2a0" => [0.0, 0.0, 3370.39, 4767.87], + "a0" => [0.0, 0.0, 2383.94, 3370.39], + "a1" => [0.0, 0.0, 1683.78, 2383.94], + "a2" => [0.0, 0.0, 1190.55, 1683.78], + "a3" => [0.0, 0.0, 841.89, 1190.55], + "a4" => [0.0, 0.0, 595.28, 841.89], + "a5" => [0.0, 0.0, 419.53, 595.28], + "a6" => [0.0, 0.0, 297.64, 419.53], + "a7" => [0.0, 0.0, 209.76, 297.64], + "a8" => [0.0, 0.0, 147.40, 209.76], + "a9" => [0.0, 0.0, 104.88, 147.40], + "a10" => [0.0, 0.0, 73.70, 104.88], + "b0" => [0.0, 0.0, 2834.65, 4008.19], + "b1" => [0.0, 0.0, 2004.09, 2834.65], + "b2" => [0.0, 0.0, 1417.32, 2004.09], + "b3" => [0.0, 0.0, 1000.63, 1417.32], + "b4" => [0.0, 0.0, 708.66, 1000.63], + "b5" => [0.0, 0.0, 498.90, 708.66], + "b6" => [0.0, 0.0, 354.33, 498.90], + "b7" => [0.0, 0.0, 249.45, 354.33], + "b8" => [0.0, 0.0, 175.75, 249.45], + "b9" => [0.0, 0.0, 124.72, 175.75], + "b10" => [0.0, 0.0, 87.87, 124.72], + "c0" => [0.0, 0.0, 2599.37, 3676.54], + "c1" => [0.0, 0.0, 1836.85, 2599.37], + "c2" => [0.0, 0.0, 1298.27, 1836.85], + "c3" => [0.0, 0.0, 918.43, 1298.27], + "c4" => [0.0, 0.0, 649.13, 918.43], + "c5" => [0.0, 0.0, 459.21, 649.13], + "c6" => [0.0, 0.0, 323.15, 459.21], + "c7" => [0.0, 0.0, 229.61, 323.15], + "c8" => [0.0, 0.0, 161.57, 229.61], + "c9" => [0.0, 0.0, 113.39, 161.57], + "c10" => [0.0, 0.0, 79.37, 113.39], + "ra0" => [0.0, 0.0, 2437.80, 3458.27], + "ra1" => [0.0, 0.0, 1729.13, 2437.80], + "ra2" => [0.0, 0.0, 1218.90, 1729.13], + "ra3" => [0.0, 0.0, 864.57, 1218.90], + "ra4" => [0.0, 0.0, 609.45, 864.57], + "sra0" => [0.0, 0.0, 2551.18, 3628.35], + "sra1" => [0.0, 0.0, 1814.17, 2551.18], + "sra2" => [0.0, 0.0, 1275.59, 1814.17], + "sra3" => [0.0, 0.0, 907.09, 1275.59], + "sra4" => [0.0, 0.0, 637.80, 907.09], + "letter" => [0.0, 0.0, 612.00, 792.00], + "half-letter" => [0.0, 0.0, 396.00, 612.00], + "legal" => [0.0, 0.0, 612.00, 1008.00], + "ledger" => [0.0, 0.0, 1224.00, 792.00], + "tabloid" => [0.0, 0.0, 792.00, 1224.00], + "executive" => [0.0, 0.0, 521.86, 756.00], + "folio" => [0.0, 0.0, 612.00, 936.00], + "commercial #10 envelope" => [0.0, 0.0, 684.00, 297.00], + "catalog #10 1/2 envelope" => [0.0, 0.0, 648.00, 864.00], + "8.5x11" => [0.0, 0.0, 612.00, 792.00], + "8.5x14" => [0.0, 0.0, 612.00, 1008.00], + "11x17" => [0.0, 0.0, 792.00, 1224.00], + ]; + + /** + * The Dompdf object + * + * @var Dompdf + */ + protected $_dompdf; + + /** + * Instance of Cpdf class + * + * @var \Dompdf\Cpdf + */ + protected $_pdf; + + /** + * PDF width, in points + * + * @var float + */ + protected $_width; + + /** + * PDF height, in points + * + * @var float + */ + protected $_height; + + /** + * Current page number + * + * @var int + */ + protected $_page_number; + + /** + * Total number of pages + * + * @var int + */ + protected $_page_count; + + /** + * Array of pages for accessing after rendering is initially complete + * + * @var array + */ + protected $_pages; + + /** + * Currently-applied opacity level (0 - 1) + * + * @var float + */ + protected $_current_opacity = 1; + + public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null) + { + if (is_array($paper)) { + $size = array_map("floatval", $paper); + } else { + $paper = strtolower($paper); + $size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"]; + } + + if (strtolower($orientation) === "landscape") { + [$size[2], $size[3]] = [$size[3], $size[2]]; + } + + if ($dompdf === null) { + $this->_dompdf = new Dompdf(); + } else { + $this->_dompdf = $dompdf; + } + + $this->_pdf = new \Dompdf\Cpdf( + $size, + true, + $this->_dompdf->getOptions()->getFontCache(), + $this->_dompdf->getOptions()->getTempDir() + ); + + $this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $this->_dompdf->version)); + $time = substr_replace(date('YmdHisO'), '\'', -2, 0) . '\''; + $this->_pdf->addInfo("CreationDate", "D:$time"); + $this->_pdf->addInfo("ModDate", "D:$time"); + + $this->_width = $size[2] - $size[0]; + $this->_height = $size[3] - $size[1]; + + $this->_page_number = $this->_page_count = 1; + + $this->_pages = [$this->_pdf->getFirstPageId()]; + } + + public function get_dompdf() + { + return $this->_dompdf; + } + + /** + * Returns the Cpdf instance + * + * @return \Dompdf\Cpdf + */ + public function get_cpdf() + { + return $this->_pdf; + } + + public function add_info(string $label, string $value): void + { + $this->_pdf->addInfo($label, $value); + } + + /** + * Opens a new 'object' + * + * While an object is open, all drawing actions are recorded in the object, + * as opposed to being drawn on the current page. Objects can be added + * later to a specific page or to several pages. + * + * The return value is an integer ID for the new object. + * + * @see CPDF::close_object() + * @see CPDF::add_object() + * + * @return int + */ + public function open_object() + { + $ret = $this->_pdf->openObject(); + $this->_pdf->saveState(); + return $ret; + } + + /** + * Reopens an existing 'object' + * + * @see CPDF::open_object() + * @param int $object the ID of a previously opened object + */ + public function reopen_object($object) + { + $this->_pdf->reopenObject($object); + $this->_pdf->saveState(); + } + + /** + * Closes the current 'object' + * + * @see CPDF::open_object() + */ + public function close_object() + { + $this->_pdf->restoreState(); + $this->_pdf->closeObject(); + } + + /** + * Adds a specified 'object' to the document + * + * $object int specifying an object created with {@link + * CPDF::open_object()}. $where can be one of: + * - 'add' add to current page only + * - 'all' add to every page from the current one onwards + * - 'odd' add to all odd numbered pages from now on + * - 'even' add to all even numbered pages from now on + * - 'next' add the object to the next page only + * - 'nextodd' add to all odd numbered pages from the next one + * - 'nexteven' add to all even numbered pages from the next one + * + * @see Cpdf::addObject() + * + * @param int $object + * @param string $where + */ + public function add_object($object, $where = 'all') + { + $this->_pdf->addObject($object, $where); + } + + /** + * Stops the specified 'object' from appearing in the document. + * + * The object will stop being displayed on the page following the current + * one. + * + * @param int $object + */ + public function stop_object($object) + { + $this->_pdf->stopObject($object); + } + + /** + * Serialize the pdf object's current state for retrieval later + */ + public function serialize_object($id) + { + return $this->_pdf->serializeObject($id); + } + + public function reopen_serialized_object($obj) + { + return $this->_pdf->restoreSerializedObject($obj); + } + + //........................................................................ + + public function get_width() + { + return $this->_width; + } + + public function get_height() + { + return $this->_height; + } + + public function get_page_number() + { + return $this->_page_number; + } + + public function get_page_count() + { + return $this->_page_count; + } + + /** + * Sets the current page number + * + * @param int $num + */ + public function set_page_number($num) + { + $this->_page_number = $num; + } + + public function set_page_count($count) + { + $this->_page_count = $count; + } + + /** + * Sets the stroke color + * + * See {@link Style::set_color()} for the format of the color array. + * + * @param array $color + */ + protected function _set_stroke_color($color) + { + $this->_pdf->setStrokeColor($color); + $alpha = isset($color["alpha"]) ? $color["alpha"] : 1; + $alpha *= $this->_current_opacity; + $this->_set_line_transparency("Normal", $alpha); + } + + /** + * Sets the fill colour + * + * See {@link Style::set_color()} for the format of the colour array. + * + * @param array $color + */ + protected function _set_fill_color($color) + { + $this->_pdf->setColor($color); + $alpha = isset($color["alpha"]) ? $color["alpha"] : 1; + $alpha *= $this->_current_opacity; + $this->_set_fill_transparency("Normal", $alpha); + } + + /** + * Sets line transparency + * @see Cpdf::setLineTransparency() + * + * Valid blend modes are (case-sensitive): + * + * Normal, Multiply, Screen, Overlay, Darken, Lighten, + * ColorDodge, ColorBurn, HardLight, SoftLight, Difference, + * Exclusion + * + * @param string $mode the blending mode to use + * @param float $opacity 0.0 fully transparent, 1.0 fully opaque + */ + protected function _set_line_transparency($mode, $opacity) + { + $this->_pdf->setLineTransparency($mode, $opacity); + } + + /** + * Sets fill transparency + * @see Cpdf::setFillTransparency() + * + * Valid blend modes are (case-sensitive): + * + * Normal, Multiply, Screen, Overlay, Darken, Lighten, + * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, + * Exclusion + * + * @param string $mode the blending mode to use + * @param float $opacity 0.0 fully transparent, 1.0 fully opaque + */ + protected function _set_fill_transparency($mode, $opacity) + { + $this->_pdf->setFillTransparency($mode, $opacity); + } + + /** + * Sets the line style + * + * @see Cpdf::setLineStyle() + * + * @param float $width + * @param string $cap + * @param string $join + * @param array $dash + */ + protected function _set_line_style($width, $cap, $join, $dash) + { + $this->_pdf->setLineStyle($width, $cap, $join, $dash); + } + + public function set_opacity(float $opacity, string $mode = "Normal"): void + { + $this->_set_line_transparency($mode, $opacity); + $this->_set_fill_transparency($mode, $opacity); + $this->_current_opacity = $opacity; + } + + public function set_default_view($view, $options = []) + { + array_unshift($options, $view); + call_user_func_array([$this->_pdf, "openHere"], $options); + } + + /** + * Remaps y coords from 4th to 1st quadrant + * + * @param float $y + * @return float + */ + protected function y($y) + { + return $this->_height - $y; + } + + public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt") + { + $this->_set_stroke_color($color); + $this->_set_line_style($width, $cap, "", $style); + + $this->_pdf->line($x1, $this->y($y1), + $x2, $this->y($y2)); + $this->_set_line_transparency("Normal", $this->_current_opacity); + } + + public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt") + { + $this->_set_stroke_color($color); + $this->_set_line_style($width, $cap, "", $style); + + $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false); + $this->_set_line_transparency("Normal", $this->_current_opacity); + } + + public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt") + { + $this->_set_stroke_color($color); + $this->_set_line_style($width, $cap, "", $style); + $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h); + $this->_set_line_transparency("Normal", $this->_current_opacity); + } + + public function filled_rectangle($x1, $y1, $w, $h, $color) + { + $this->_set_fill_color($color); + $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h); + $this->_set_fill_transparency("Normal", $this->_current_opacity); + } + + public function clipping_rectangle($x1, $y1, $w, $h) + { + $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h); + } + + public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) + { + $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL); + } + + public function clipping_polygon(array $points): void + { + // Adjust y values + for ($i = 1; $i < count($points); $i += 2) { + $points[$i] = $this->y($points[$i]); + } + + $this->_pdf->clippingPolygon($points); + } + + public function clipping_end() + { + $this->_pdf->clippingEnd(); + } + + public function save() + { + $this->_pdf->saveState(); + } + + public function restore() + { + $this->_pdf->restoreState(); + } + + public function rotate($angle, $x, $y) + { + $this->_pdf->rotate($angle, $x, $y); + } + + public function skew($angle_x, $angle_y, $x, $y) + { + $this->_pdf->skew($angle_x, $angle_y, $x, $y); + } + + public function scale($s_x, $s_y, $x, $y) + { + $this->_pdf->scale($s_x, $s_y, $x, $y); + } + + public function translate($t_x, $t_y) + { + $this->_pdf->translate($t_x, $t_y); + } + + public function transform($a, $b, $c, $d, $e, $f) + { + $this->_pdf->transform([$a, $b, $c, $d, $e, $f]); + } + + public function polygon($points, $color, $width = null, $style = [], $fill = false) + { + $this->_set_fill_color($color); + $this->_set_stroke_color($color); + + if (!$fill && isset($width)) { + $this->_set_line_style($width, "square", "miter", $style); + } + + // Adjust y values + for ($i = 1; $i < count($points); $i += 2) { + $points[$i] = $this->y($points[$i]); + } + + $this->_pdf->polygon($points, $fill); + + $this->_set_fill_transparency("Normal", $this->_current_opacity); + $this->_set_line_transparency("Normal", $this->_current_opacity); + } + + public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false) + { + $this->_set_fill_color($color); + $this->_set_stroke_color($color); + + if (!$fill && isset($width)) { + $this->_set_line_style($width, "round", "round", $style); + } + + $this->_pdf->ellipse($x, $this->y($y), $r, 0, 0, 8, 0, 360, 1, $fill); + + $this->_set_fill_transparency("Normal", $this->_current_opacity); + $this->_set_line_transparency("Normal", $this->_current_opacity); + } + + /** + * Convert image to a PNG image + * + * @param string $image_url + * @param string $type + * + * @return string|null The url of the newly converted image + */ + protected function _convert_to_png($image_url, $type) + { + $filename = Cache::getTempImage($image_url); + + if ($filename !== null && file_exists($filename)) { + return $filename; + } + + $func_name = "imagecreatefrom$type"; + + set_error_handler([Helpers::class, "record_warnings"]); + + if (!function_exists($func_name)) { + if (!method_exists(Helpers::class, $func_name)) { + throw new Exception("Function $func_name() not found. Cannot convert $type image: $image_url. Please install the image PHP extension."); + } + $func_name = [Helpers::class, $func_name]; + } + + try { + $im = call_user_func($func_name, $image_url); + + if ($im) { + imageinterlace($im, false); + + $tmp_dir = $this->_dompdf->getOptions()->getTempDir(); + $tmp_name = @tempnam($tmp_dir, "{$type}_dompdf_img_"); + @unlink($tmp_name); + $filename = "$tmp_name.png"; + + imagepng($im, $filename); + imagedestroy($im); + } else { + $filename = null; + } + } finally { + restore_error_handler(); + } + + if ($filename !== null) { + Cache::addTempImage($image_url, $filename); + } + + return $filename; + } + + public function image($img, $x, $y, $w, $h, $resolution = "normal") + { + [$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext()); + + $debug_png = $this->_dompdf->getOptions()->getDebugPng(); + + if ($debug_png) { + print "[image:$img|$width|$height|$type]"; + } + + switch ($type) { + case "jpeg": + if ($debug_png) { + print '!!!jpg!!!'; + } + $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h); + break; + + case "webp": + /** @noinspection PhpMissingBreakStatementInspection */ + case "gif": + /** @noinspection PhpMissingBreakStatementInspection */ + case "bmp": + if ($debug_png) print "!!!{$type}!!!"; + $img = $this->_convert_to_png($img, $type); + if ($img === null) { + if ($debug_png) print '!!!conversion to PDF failed!!!'; + $this->image(Cache::$broken_image, $x, $y, $w, $h, $resolution); + break; + } + + case "png": + if ($debug_png) print '!!!png!!!'; + + $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h); + break; + + case "svg": + if ($debug_png) print '!!!SVG!!!'; + + $this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h); + break; + + default: + if ($debug_png) print '!!!unknown!!!'; + } + } + + public function select($x, $y, $w, $h, $font, $size, $color = [0, 0, 0], $opts = []) + { + $pdf = $this->_pdf; + + $font .= ".afm"; + $pdf->selectFont($font); + + if (!isset($pdf->acroFormId)) { + $pdf->addForm(); + } + + $ft = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE; + $ff = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE_COMBO; + + $id = $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color); + $pdf->setFormFieldOpt($id, $opts); + } + + public function textarea($x, $y, $w, $h, $font, $size, $color = [0, 0, 0]) + { + $pdf = $this->_pdf; + + $font .= ".afm"; + $pdf->selectFont($font); + + if (!isset($pdf->acroFormId)) { + $pdf->addForm(); + } + + $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT; + $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_MULTILINE; + + $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color); + } + + public function input($x, $y, $w, $h, $type, $font, $size, $color = [0, 0, 0]) + { + $pdf = $this->_pdf; + + $font .= ".afm"; + $pdf->selectFont($font); + + if (!isset($pdf->acroFormId)) { + $pdf->addForm(); + } + + $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT; + $ff = 0; + + switch ($type) { + case 'text': + $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT; + break; + case 'password': + $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT; + $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_PASSWORD; + break; + case 'submit': + $ft = \Dompdf\Cpdf::ACROFORM_FIELD_BUTTON; + break; + } + + $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color); + } + + public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0) + { + $pdf = $this->_pdf; + + $this->_set_fill_color($color); + + $is_font_subsetting = $this->_dompdf->getOptions()->getIsFontSubsettingEnabled(); + $pdf->selectFont($font . '.afm', '', true, $is_font_subsetting); + + $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space); + + $this->_set_fill_transparency("Normal", $this->_current_opacity); + } + + public function javascript($code) + { + $this->_pdf->addJavascript($code); + } + + //........................................................................ + + public function add_named_dest($anchorname) + { + $this->_pdf->addDestination($anchorname, "Fit"); + } + + public function add_link($url, $x, $y, $width, $height) + { + $y = $this->y($y) - $height; + + if (strpos($url, '#') === 0) { + // Local link + $name = substr($url, 1); + if ($name) { + $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height); + } + } else { + $this->_pdf->addLink($url, $x, $y, $x + $width, $y + $height); + } + } + + /** + * @throws FontNotFoundException + */ + public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) + { + $this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled()); + return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing); + } + + /** + * @throws FontNotFoundException + */ + public function get_font_height($font, $size) + { + $options = $this->_dompdf->getOptions(); + $this->_pdf->selectFont($font, '', true, $options->getIsFontSubsettingEnabled()); + + return $this->_pdf->getFontHeight($size) * $options->getFontHeightRatio(); + } + + /*function get_font_x_height($font, $size) { + $this->_pdf->selectFont($font); + $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); + return $this->_pdf->getFontXHeight($size) * $ratio; + }*/ + + /** + * @throws FontNotFoundException + */ + public function get_font_baseline($font, $size) + { + $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); + return $this->get_font_height($font, $size) / $ratio; + } + + /** + * Processes a callback or script on every page. + * + * The callback function receives the four parameters `int $pageNumber`, + * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in + * that order. If a script is passed as string, the variables `$PAGE_NUM`, + * `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing + * a script as string is deprecated and will be removed in a future version. + * + * This function can be used to add page numbers to all pages after the + * first one, for example. + * + * @param callable|string $callback The callback function or PHP script to process on every page + */ + public function page_script($callback): void + { + if (is_string($callback)) { + $this->processPageScript(function ( + int $PAGE_NUM, + int $PAGE_COUNT, + self $pdf, + FontMetrics $fontMetrics + ) use ($callback) { + eval($callback); + }); + return; + } + + $this->processPageScript($callback); + } + + public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0) + { + $this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) { + $text = str_replace( + ["{PAGE_NUM}", "{PAGE_COUNT}"], + [$pageNumber, $pageCount], + $text + ); + $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); + }); + } + + public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = []) + { + $this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) { + $this->line($x1, $y1, $x2, $y2, $color, $width, $style); + }); + } + + /** + * @return int + */ + public function new_page() + { + $this->_page_number++; + $this->_page_count++; + + $ret = $this->_pdf->newPage(); + $this->_pages[] = $ret; + return $ret; + } + + protected function processPageScript(callable $callback): void + { + $pageNumber = 1; + + foreach ($this->_pages as $pid) { + $this->reopen_object($pid); + + $fontMetrics = $this->_dompdf->getFontMetrics(); + $callback($pageNumber, $this->_page_count, $this, $fontMetrics); + + $this->close_object(); + $pageNumber++; + } + } + + public function stream($filename = "document.pdf", $options = []) + { + if (headers_sent()) { + die("Unable to stream pdf: headers already sent"); + } + + if (!isset($options["compress"])) $options["compress"] = true; + if (!isset($options["Attachment"])) $options["Attachment"] = true; + + $debug = !$options['compress']; + $tmp = ltrim($this->_pdf->output($debug)); + + header("Cache-Control: private"); + header("Content-Type: application/pdf"); + header("Content-Length: " . mb_strlen($tmp, "8bit")); + + $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf"; + $attachment = $options["Attachment"] ? "attachment" : "inline"; + header(Helpers::buildContentDispositionHeader($attachment, $filename)); + + echo $tmp; + flush(); + } + + public function output($options = []) + { + if (!isset($options["compress"])) $options["compress"] = true; + + $debug = !$options['compress']; + + return $this->_pdf->output($debug); + } + + /** + * Returns logging messages generated by the Cpdf class + * + * @return string + */ + public function get_messages() + { + return $this->_pdf->messages; + } +} diff --git a/library/vendor/dompdf/src/Adapter/GD.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/GD.php similarity index 58% rename from library/vendor/dompdf/src/Adapter/GD.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/GD.php index 92fbdbe11..8c10e4766 100644 --- a/library/vendor/dompdf/src/Adapter/GD.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/GD.php @@ -1,17 +1,15 @@ - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Adapter; use Dompdf\Canvas; use Dompdf\Dompdf; -use Dompdf\Image\Cache; use Dompdf\Helpers; +use Dompdf\Image\Cache; /** * Image rendering interface @@ -26,98 +24,98 @@ class GD implements Canvas /** * @var Dompdf */ - private $_dompdf; + protected $_dompdf; /** * Resource handle for the image * - * @var resource + * @var \GdImage|resource */ - private $_img; + protected $_img; /** * Resource handle for the image * - * @var resource[] + * @var \GdImage[]|resource[] */ - private $_imgs; + protected $_imgs; /** * Apparent canvas width in pixels * * @var int */ - private $_width; + protected $_width; /** * Apparent canvas height in pixels * * @var int */ - private $_height; + protected $_height; /** * Actual image width in pixels * * @var int */ - private $_actual_width; + protected $_actual_width; /** * Actual image height in pixels * * @var int */ - private $_actual_height; + protected $_actual_height; /** * Current page number * * @var int */ - private $_page_number; + protected $_page_number; /** * Total number of pages * * @var int */ - private $_page_count; + protected $_page_count; /** * Image antialias factor * * @var float */ - private $_aa_factor; + protected $_aa_factor; /** * Allocated colors * * @var array */ - private $_colors; + protected $_colors; /** * Background color * * @var int */ - private $_bg_color; + protected $_bg_color; /** * Background color array * * @var int */ - private $_bg_color_array; + protected $_bg_color_array; /** * Actual DPI * * @var int */ - private $dpi; + protected $dpi; /** * Amount to scale font sizes @@ -130,32 +128,31 @@ class GD implements Canvas const FONT_SCALE = 0.75; /** - * Class constructor - * - * @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc. - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param Dompdf $dompdf - * @param float $aa_factor Anti-aliasing factor, 1 for no AA - * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1 + * @param string|float[] $paper The paper size to use as either a standard paper size (see {@link CPDF::$PAPER_SIZES}) or + * an array of the form `[x1, y1, x2, y2]` (typically `[0, 0, width, height]`). + * @param string $orientation The paper orientation, either `portrait` or `landscape`. + * @param Dompdf $dompdf The Dompdf instance. + * @param float $aa_factor Anti-aliasing factor, 1 for no AA + * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1 */ - public function __construct($size = 'letter', $orientation = "portrait", Dompdf $dompdf, $aa_factor = 1.0, $bg_color = array(1, 1, 1, 0)) + public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null, $aa_factor = 1.0, $bg_color = [1, 1, 1, 0]) { - - if (!is_array($size)) { - $size = strtolower($size); - - if (isset(CPDF::$PAPER_SIZES[$size])) { - $size = CPDF::$PAPER_SIZES[$size]; - } else { - $size = CPDF::$PAPER_SIZES["letter"]; - } + if (is_array($paper)) { + $size = array_map("floatval", $paper); + } else { + $paper = strtolower($paper); + $size = CPDF::$PAPER_SIZES[$paper] ?? CPDF::$PAPER_SIZES["letter"]; } if (strtolower($orientation) === "landscape") { - list($size[2], $size[3]) = array($size[3], $size[2]); + [$size[2], $size[3]] = [$size[3], $size[2]]; } - $this->_dompdf = $dompdf; + if ($dompdf === null) { + $this->_dompdf = new Dompdf(); + } else { + $this->_dompdf = $dompdf; + } $this->dpi = $this->get_dompdf()->getOptions()->getDpi(); @@ -174,9 +171,11 @@ class GD implements Canvas $this->_actual_width = $this->_upscale($this->_width); $this->_actual_height = $this->_upscale($this->_height); + $this->_page_number = $this->_page_count = 0; + if (is_null($bg_color) || !is_array($bg_color)) { // Pure white bg - $bg_color = array(1, 1, 1, 0); + $bg_color = [1, 1, 1, 0]; } $this->_bg_color_array = $bg_color; @@ -184,18 +183,15 @@ class GD implements Canvas $this->new_page(); } - /** - * @return Dompdf - */ public function get_dompdf() { return $this->_dompdf; } /** - * Return the GF image resource + * Return the GD image resource * - * @return resource + * @return \GdImage|resource */ public function get_image() { @@ -205,36 +201,28 @@ class GD implements Canvas /** * Return the image's width in pixels * - * @return float + * @return int */ public function get_width() { - return $this->_width / $this->_aa_factor; + return round($this->_width / $this->_aa_factor); } /** * Return the image's height in pixels * - * @return float + * @return int */ public function get_height() { - return $this->_height / $this->_aa_factor; + return round($this->_height / $this->_aa_factor); } - /** - * Returns the current page number - * @return int - */ public function get_page_number() { return $this->_page_number; } - /** - * Returns the total number of pages in the document - * @return int - */ public function get_page_count() { return $this->_page_count; @@ -250,23 +238,12 @@ class GD implements Canvas $this->_page_number = $num; } - /** - * Sets the page count - * - * @param int $count - */ public function set_page_count($count) { $this->_page_count = $count; } - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - public function set_opacity($opacity, $mode = "Normal") + public function set_opacity(float $opacity, string $mode = "Normal"): void { // FIXME } @@ -276,9 +253,9 @@ class GD implements Canvas * previously allocated colors in $this->_colors. * * @param array $color The new current color - * @return int The allocated color + * @return int The allocated color */ - private function _allocate_color($color) + protected function _allocate_color($color) { $a = isset($color["alpha"]) ? $color["alpha"] : 1; @@ -288,10 +265,10 @@ class GD implements Canvas list($r, $g, $b) = $color; - $r *= 255; - $g *= 255; - $b *= 255; - $a = 127 - ($a * 127); + $r = round($r * 255); + $g = round($g * 255); + $b = round($b * 255); + $a = round(127 - ($a * 127)); // Clip values $r = $r > 255 ? 255 : $r; @@ -323,11 +300,11 @@ class GD implements Canvas * Scales value up to the current canvas DPI from 72 DPI * * @param float $length - * @return float + * @return int */ - private function _upscale($length) + protected function _upscale($length) { - return ($length * $this->dpi) / 72 * $this->_aa_factor; + return round(($length * $this->dpi) / 72 * $this->_aa_factor); } /** @@ -336,28 +313,59 @@ class GD implements Canvas * @param float $length * @return float */ - private function _downscale($length) + protected function _downscale($length) { - return ($length / $this->dpi * 72) / $this->_aa_factor; + return round(($length / $this->dpi * 72) / $this->_aa_factor); } - /** - * Draws a line from x1,y1 to x2,y2 - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the format of the - * $style parameter (aka dash). - * - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - public function line($x1, $y1, $x2, $y2, $color, $width, $style = null) + protected function convertStyle(array $style, int $color, int $width): array { + $gdStyle = []; + + if (count($style) === 1) { + $style[] = $style[0]; + } + + foreach ($style as $index => $s) { + $d = $this->_upscale($s); + + for ($i = 0; $i < $d; $i++) { + for ($j = 0; $j < $width; $j++) { + $gdStyle[] = $index % 2 === 0 + ? $color + : IMG_COLOR_TRANSPARENT; + } + } + } + + return $gdStyle; + } + + public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt") + { + // Account for the fact that round and square caps are expected to + // extend outwards + if ($cap === "round" || $cap === "square") { + // Shift line by half width + $w = $width / 2; + $a = $x2 - $x1; + $b = $y2 - $y1; + $c = sqrt($a ** 2 + $b ** 2); + $dx = $a * $w / $c; + $dy = $b * $w / $c; + + $x1 -= $dx; + $x2 -= $dx; + $y1 -= $dy; + $y2 -= $dy; + + // Adapt dash pattern + if (is_array($style)) { + foreach ($style as $index => &$s) { + $s = $index % 2 === 0 ? $s + $width : $s - $width; + } + } + } // Scale by the AA factor and DPI $x1 = $this->_upscale($x1); @@ -370,34 +378,7 @@ class GD implements Canvas // Convert the style array if required if (is_array($style) && count($style) > 0) { - $gd_style = array(); - - if (count($style) == 1) { - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $c; - } - - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $this->_bg_color; - } - } else { - $i = 0; - foreach ($style as $length) { - if ($i % 2 == 0) { - // 'On' pattern - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $c; - } - - } else { - // Off pattern - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $this->_bg_color; - } - } - $i++; - } - } + $gd_style = $this->convertStyle($style, $c, $width); if (!empty($gd_style)) { imagesetstyle($this->get_image(), $gd_style); @@ -410,39 +391,59 @@ class GD implements Canvas imageline($this->get_image(), $x1, $y1, $x2, $y2, $c); } - /** - * @param float $x1 - * @param float $y1 - * @param float $r1 - * @param float $r2 - * @param float $astart - * @param float $aend - * @param array $color - * @param float $width - * @param array $style - */ - public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) + public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt") { - // @todo + // Account for the fact that round and square caps are expected to + // extend outwards + if ($cap === "round" || $cap === "square") { + // Adapt dash pattern + if (is_array($style)) { + foreach ($style as $index => &$s) { + $s = $index % 2 === 0 ? $s + $width : $s - $width; + } + } + } + + // Scale by the AA factor and DPI + $x = $this->_upscale($x); + $y = $this->_upscale($y); + $w = $this->_upscale($r1 * 2); + $h = $this->_upscale($r2 * 2); + $width = $this->_upscale($width); + + // Adapt angles as imagearc counts clockwise + $start = 360 - $aend; + $end = 360 - $astart; + + $c = $this->_allocate_color($color); + + // Convert the style array if required + if (is_array($style) && count($style) > 0) { + $gd_style = $this->convertStyle($style, $c, $width); + + if (!empty($gd_style)) { + imagesetstyle($this->get_image(), $gd_style); + $c = IMG_COLOR_STYLED; + } + } + + imagesetthickness($this->get_image(), $width); + + imagearc($this->get_image(), $x, $y, $w, $h, $start, $end, $c); } - /** - * Draws a rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - * @param float $width - * @param array $style - */ - public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) + public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt") { + // Account for the fact that round and square caps are expected to + // extend outwards + if ($cap === "round" || $cap === "square") { + // Adapt dash pattern + if (is_array($style)) { + foreach ($style as $index => &$s) { + $s = $index % 2 === 0 ? $s + $width : $s - $width; + } + } + } // Scale by the AA factor and DPI $x1 = $this->_upscale($x1); @@ -455,13 +456,7 @@ class GD implements Canvas // Convert the style array if required if (is_array($style) && count($style) > 0) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } + $gd_style = $this->convertStyle($style, $c, $width); if (!empty($gd_style)) { imagesetstyle($this->get_image(), $gd_style); @@ -471,20 +466,18 @@ class GD implements Canvas imagesetthickness($this->get_image(), $width); - imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c); + if ($c === IMG_COLOR_STYLED) { + imagepolygon($this->get_image(), [ + $x1, $y1, + $x1 + $w, $y1, + $x1 + $w, $y1 + $h, + $x1, $y1 + $h + ], $c); + } else { + imagerectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c); + } } - /** - * Draws a filled rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - */ public function filled_rectangle($x1, $y1, $w, $h, $color) { // Scale by the AA factor and DPI @@ -498,14 +491,6 @@ class GD implements Canvas imagefilledrectangle($this->get_image(), $x1, $y1, $x1 + $w, $y1 + $h, $c); } - /** - * Starts a clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - */ public function clipping_rectangle($x1, $y1, $w, $h) { // @todo @@ -516,127 +501,65 @@ class GD implements Canvas // @todo } - /** - * Ends the last clipping shape - */ + public function clipping_polygon(array $points): void + { + // @todo + } + public function clipping_end() { // @todo } - /** - * - */ public function save() { $this->get_dompdf()->getOptions()->setDpi(72); } - /** - * - */ public function restore() { $this->get_dompdf()->getOptions()->setDpi($this->dpi); } - /** - * @param $angle - * @param $x - * @param $y - */ public function rotate($angle, $x, $y) { // @todo } - /** - * @param $angle_x - * @param $angle_y - * @param $x - * @param $y - */ public function skew($angle_x, $angle_y, $x, $y) { // @todo } - /** - * @param $s_x - * @param $s_y - * @param $x - * @param $y - */ public function scale($s_x, $s_y, $x, $y) { // @todo } - /** - * @param $t_x - * @param $t_y - */ public function translate($t_x, $t_y) { // @todo } - /** - * @param $a - * @param $b - * @param $c - * @param $d - * @param $e - * @param $f - */ public function transform($a, $b, $c, $d, $e, $f) { // @todo } - /** - * Draws a polygon - * - * The polygon is formed by joining all the points stored in the $points - * array. $points has the following structure: - * - * array(0 => x1, - * 1 => y1, - * 2 => x2, - * 3 => y2, - * ... - * ); - * - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param array $points - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the polygon if true - */ - public function polygon($points, $color, $width = null, $style = null, $fill = false) + public function polygon($points, $color, $width = null, $style = [], $fill = false) { - // Scale each point by the AA factor and DPI foreach (array_keys($points) as $i) { $points[$i] = $this->_upscale($points[$i]); } + $width = isset($width) ? $this->_upscale($width) : null; + $c = $this->_allocate_color($color); // Convert the style array if required - if (is_array($style) && count($style) > 0 && !$fill) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } + if (is_array($style) && count($style) > 0 && isset($width) && !$fill) { + $gd_style = $this->convertStyle($style, $c, $width); if (!empty($gd_style)) { imagesetstyle($this->get_image(), $gd_style); @@ -644,48 +567,28 @@ class GD implements Canvas } } - imagesetthickness($this->get_image(), $width); + imagesetthickness($this->get_image(), isset($width) ? $width : 0); if ($fill) { - imagefilledpolygon($this->get_image(), $points, count($points) / 2, $c); + imagefilledpolygon($this->get_image(), $points, $c); } else { - imagepolygon($this->get_image(), $points, count($points) / 2, $c); + imagepolygon($this->get_image(), $points, $c); } } - /** - * Draws a circle at $x,$y with radius $r - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x - * @param float $y - * @param float $r - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the circle if true - */ - public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) + public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false) { // Scale by the AA factor and DPI $x = $this->_upscale($x); $y = $this->_upscale($y); - $r = $this->_upscale($r); + $d = $this->_upscale(2 * $r); + $width = isset($width) ? $this->_upscale($width) : null; $c = $this->_allocate_color($color); // Convert the style array if required - if (is_array($style) && count($style) > 0 && !$fill) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } + if (is_array($style) && count($style) > 0 && isset($width) && !$fill) { + $gd_style = $this->convertStyle($style, $c, $width); if (!empty($gd_style)) { imagesetstyle($this->get_image(), $gd_style); @@ -693,34 +596,21 @@ class GD implements Canvas } } - imagesetthickness($this->get_image(), $width); + imagesetthickness($this->get_image(), isset($width) ? $width : 0); if ($fill) { - imagefilledellipse($this->get_image(), $x, $y, $r, $r, $c); + imagefilledellipse($this->get_image(), $x, $y, $d, $d, $c); } else { - imageellipse($this->get_image(), $x, $y, $r, $r, $c); + imageellipse($this->get_image(), $x, $y, $d, $d, $c); } } /** - * Add an image to the pdf. - * The image is placed at the specified x and y coordinates with the - * given width and height. - * - * @param string $img_url the path to the image - * @param float $x x position - * @param float $y y position - * @param int $w width (in pixels) - * @param int $h height (in pixels) - * @param string $resolution - * @return void - * * @throws \Exception - * @internal param string $img_type the type (e.g. extension) of the image */ - public function image($img_url, $x, $y, $w, $h, $resolution = "normal") + public function image($img, $x, $y, $w, $h, $resolution = "normal") { - $img_type = Cache::detect_type($img_url, $this->get_dompdf()->getHttpContext()); + $img_type = Cache::detect_type($img, $this->get_dompdf()->getHttpContext()); if (!$img_type) { return; @@ -728,12 +618,12 @@ class GD implements Canvas $func_name = "imagecreatefrom$img_type"; if (!function_exists($func_name)) { - if (!method_exists("Dompdf\Helpers", $func_name)) { - throw new \Exception("Function $func_name() not found. Cannot convert $img_type image: $img_url. Please install the image PHP extension."); + if (!method_exists(Helpers::class, $func_name)) { + throw new \Exception("Function $func_name() not found. Cannot convert $img_type image: $img. Please install the image PHP extension."); } - $func_name = "\\Dompdf\\Helpers::" . $func_name; + $func_name = [Helpers::class, $func_name]; } - $src = @call_user_func($func_name, $img_url); + $src = @call_user_func($func_name, $img); if (!$src) { return; // Probably should add to $_dompdf_errors or whatever here @@ -752,30 +642,14 @@ class GD implements Canvas imagecopyresampled($this->get_image(), $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h); } - /** - * Writes text at the specified x and y coordinates - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_spacing word spacing adjustment - * @param float $char_spacing - * @param float $angle Text angle - * - * @return void - */ - public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) + public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) { // Scale by the AA factor and DPI $x = $this->_upscale($x); $y = $this->_upscale($y); $size = $this->_upscale($size) * self::FONT_SCALE; - $h = $this->get_font_height_actual($font, $size); + $h = round($this->get_font_height_actual($font, $size)); $c = $this->_allocate_color($color); // imagettftext() converts numeric entities to their respective @@ -784,7 +658,7 @@ class GD implements Canvas // eg: &#160; will render   rather than its character. $text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $text); - $text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8'); + $text = mb_encode_numericentity($text, [0x0080, 0xff, 0, 0xff], 'UTF-8'); $font = $this->get_ttf_file($font); @@ -797,61 +671,26 @@ class GD implements Canvas // Not implemented } - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ public function add_named_dest($anchorname) { // Not implemented } - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ public function add_link($url, $x, $y, $width, $height) { // Not implemented } - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producer, etc.) - * @param string $value the text to set - */ - public function add_info($label, $value) + public function add_info(string $label, string $value): void { // N/A } - /** - * @param string $view - * @param array $options - */ - public function set_default_view($view, $options = array()) + public function set_default_view($view, $options = []) { // N/A } - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing word spacing, if any - * @param float $char_spacing char spacing, if any - * - * @return float - */ public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) { $font = $this->get_ttf_file($font); @@ -863,7 +702,7 @@ class GD implements Canvas // eg: &#160; will render   rather than its character. $text = preg_replace('/&(#(?:x[a-fA-F0-9]+|[0-9]+);)/', '&\1', $text); - $text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); + $text = mb_encode_numericentity($text, [0x0080, 0xffff, 0, 0xffff], 'UTF-8'); // FIXME: word spacing list($x1, , $x2) = imagettfbbox($size, 0, $font, $text); @@ -873,11 +712,15 @@ class GD implements Canvas } /** - * @param $font + * @param string|null $font * @return string */ public function get_ttf_file($font) { + if ($font === null) { + $font = ""; + } + if ( stripos($font, ".ttf") === false ) { $font .= ".ttf"; } @@ -901,13 +744,6 @@ class GD implements Canvas return $font; } - /** - * Calculates font height, in points - * - * @param string $font - * @param float $size - * @return float - */ public function get_font_height($font, $size) { $size = $this->_upscale($size) * self::FONT_SCALE; @@ -917,7 +753,13 @@ class GD implements Canvas return $this->_downscale($height); } - private function get_font_height_actual($font, $size) + /** + * @param string $font + * @param float $size + * + * @return float + */ + protected function get_font_height_actual($font, $size) { $font = $this->get_ttf_file($font); $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); @@ -927,22 +769,12 @@ class GD implements Canvas return ($y2 - $y1) * $ratio; } - /** - * @param string $font - * @param float $size - * @return float - */ public function get_font_baseline($font, $size) { $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); return $this->get_font_height($font, $size) / $ratio; } - /** - * Starts a new page - * - * Subsequent drawing operations will appear on the new page. - */ public function new_page() { $this->_page_number++; @@ -973,12 +805,17 @@ class GD implements Canvas // N/A } - public function page_text() + public function page_script($callback): void { // N/A } - public function page_line() + public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0) + { + // N/A + } + + public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = []) { // N/A } @@ -987,10 +824,10 @@ class GD implements Canvas * Streams the image to the client. * * @param string $filename The filename to present to the client. - * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only); + * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only); * 'page' => Number of the page to output (defaults to the first); 'Attachment': 1 or 0 (default 1). */ - public function stream($filename, $options = array()) + public function stream($filename, $options = []) { if (headers_sent()) { die("Unable to stream image: headers already sent"); @@ -1016,7 +853,7 @@ class GD implements Canvas header("Cache-Control: private"); header("Content-Type: $contentType"); - $filename = str_replace(array("\n", "'"), "", basename($filename, ".$type")) . $extension; + $filename = str_replace(["\n", "'"], "", basename($filename, ".$type")) . $extension; $attachment = $options["Attachment"] ? "attachment" : "inline"; header(Helpers::buildContentDispositionHeader($attachment, $filename)); @@ -1031,7 +868,7 @@ class GD implements Canvas * 'page' => Number of the page to output (defaults to the first). * @return string */ - public function output($options = array()) + public function output($options = []) { ob_start(); @@ -1046,7 +883,7 @@ class GD implements Canvas * @param array $options Associative array: 'type' => jpeg|jpg|png; 'quality' => 0 - 100 (JPEG only); * 'page' => Number of the page to output (defaults to the first). */ - private function _output($options = array()) + protected function _output($options = []) { if (!isset($options["type"])) $options["type"] = "png"; if (!isset($options["page"])) $options["page"] = 1; @@ -1060,8 +897,8 @@ class GD implements Canvas // Perform any antialiasing if ($this->_aa_factor != 1) { - $dst_w = $this->_actual_width / $this->_aa_factor; - $dst_h = $this->_actual_height / $this->_aa_factor; + $dst_w = round($this->_actual_width / $this->_aa_factor); + $dst_h = round($this->_actual_height / $this->_aa_factor); $dst = imagecreatetruecolor($dst_w, $dst_h); imagecopyresampled($dst, $img, 0, 0, 0, 0, $dst_w, $dst_h, diff --git a/library/vendor/dompdf/src/Adapter/PDFLib.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/PDFLib.php similarity index 72% rename from library/vendor/dompdf/src/Adapter/PDFLib.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/PDFLib.php index e999c580e..ee5ae8dd2 100644 --- a/library/vendor/dompdf/src/Adapter/PDFLib.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Adapter/PDFLib.php @@ -1,20 +1,17 @@ - * @author Helmut Tischer + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\Adapter; use Dompdf\Canvas; use Dompdf\Dompdf; -use Dompdf\Helpers; use Dompdf\Exception; +use Dompdf\FontMetrics; +use Dompdf\Helpers; use Dompdf\Image\Cache; -use Dompdf\PhpEvaluator; /** * PDF rendering interface @@ -37,9 +34,9 @@ class PDFLib implements Canvas /** * Dimensions of paper sizes in points * - * @var array; + * @var array */ - static public $PAPER_SIZES = array(); // Set to Dompdf\Adapter\CPDF::$PAPER_SIZES below. + public static $PAPER_SIZES = []; // Set to Dompdf\Adapter\CPDF::$PAPER_SIZES below. /** * Whether to create PDFs in memory or on disk @@ -53,7 +50,7 @@ class PDFLib implements Canvas * * @var null|int */ - static private $MAJOR_VERSION = null; + protected static $MAJOR_VERSION = null; /** @@ -61,7 +58,7 @@ class PDFLib implements Canvas * * @var array */ - static public $nativeFontsTpPDFLib = array( + public static $nativeFontsTpPDFLib = [ "courier" => "Courier", "courier-bold" => "Courier-Bold", "courier-oblique" => "Courier-Oblique", @@ -78,151 +75,139 @@ class PDFLib implements Canvas "symbol" => "Symbol", "zapfdinbats" => "ZapfDingbats", "zapfdingbats" => "ZapfDingbats", - ); + ]; /** * @var \Dompdf\Dompdf */ - private $_dompdf; + protected $_dompdf; /** * Instance of PDFLib class * * @var \PDFLib */ - private $_pdf; + protected $_pdf; /** * Name of temporary file used for PDFs created on disk * * @var string */ - private $_file; + protected $_file; /** * PDF width, in points * * @var float */ - private $_width; + protected $_width; /** * PDF height, in points * * @var float */ - private $_height; + protected $_height; /** * Last fill color used * * @var array */ - private $_last_fill_color; + protected $_last_fill_color; /** * Last stroke color used * * @var array */ - private $_last_stroke_color; + protected $_last_stroke_color; /** * The current opacity level * - * @var array + * @var float|null */ - private $_current_opacity; + protected $_current_opacity; /** * Cache of image handles * * @var array */ - private $_imgs; + protected $_imgs; /** * Cache of font handles * * @var array */ - private $_fonts; + protected $_fonts; /** * Cache of fontFile checks * * @var array */ - private $_fontsFiles; + protected $_fontsFiles; /** * List of objects (templates) to add to multiple pages * * @var array */ - private $_objs; + protected $_objs; /** * List of gstate objects created for this PDF (for reuse) * * @var array */ - private $_gstates = array(); + protected $_gstates = []; /** * Current page number * * @var int */ - private $_page_number; + protected $_page_number; /** * Total number of pages * * @var int */ - private $_page_count; + protected $_page_count; /** - * Text to display on every page + * Array of pages for accessing after rendering is initially complete * * @var array */ - private $_page_text; + protected $_pages; - /** - * Array of pages for accesing after rendering is initially complete - * - * @var array - */ - private $_pages; - - /** - * Class constructor - * - * @param string|array $paper The size of paper to use either a string (see {@link Dompdf\Adapter\CPDF::$PAPER_SIZES}) or - * an array(xmin,ymin,xmax,ymax) - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param Dompdf $dompdf - */ - public function __construct($paper = "letter", $orientation = "portrait", Dompdf $dompdf) + public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null) { if (is_array($paper)) { - $size = $paper; - } elseif (isset(self::$PAPER_SIZES[mb_strtolower($paper)])) { - $size = self::$PAPER_SIZES[mb_strtolower($paper)]; + $size = array_map("floatval", $paper); } else { - $size = self::$PAPER_SIZES["letter"]; + $paper = strtolower($paper); + $size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"]; } - if (mb_strtolower($orientation) === "landscape") { - list($size[2], $size[3]) = array($size[3], $size[2]); + if (strtolower($orientation) === "landscape") { + [$size[2], $size[3]] = [$size[3], $size[2]]; } $this->_width = $size[2] - $size[0]; $this->_height = $size[3] - $size[1]; - $this->_dompdf = $dompdf; + if ($dompdf === null) { + $this->_dompdf = new Dompdf(); + } else { + $this->_dompdf = $dompdf; + } $this->_pdf = new \PDFLib(); @@ -231,7 +216,9 @@ class PDFLib implements Canvas $this->setPDFLibParameter("license", $license); } - $this->setPDFLibParameter("textformat", "utf8"); + if ($this->getPDFLibMajorVersion() < 10) { + $this->setPDFLibParameter("textformat", "utf8"); + } if ($this->getPDFLibMajorVersion() >= 7) { $this->setPDFLibParameter("errorpolicy", "return"); // $this->_pdf->set_option('logging={filename=' . \APP_PATH . '/logs/pdflib.log classes={api=1 warning=2}}'); @@ -267,16 +254,12 @@ class PDFLib implements Canvas $this->_pdf->begin_page_ext($this->_width, $this->_height, ""); $this->_page_number = $this->_page_count = 1; - $this->_page_text = array(); - $this->_imgs = array(); - $this->_fonts = array(); - $this->_objs = array(); + $this->_imgs = []; + $this->_fonts = []; + $this->_objs = []; } - /** - * @return Dompdf - */ function get_dompdf() { return $this->_dompdf; @@ -310,13 +293,7 @@ class PDFLib implements Canvas return $this->_pdf; } - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producter, etc.) - * @param string $value the text to set - */ - public function add_info($label, $value) + public function add_info(string $label, string $value): void { $this->_pdf->set_info($label, $value); } @@ -338,9 +315,13 @@ class PDFLib implements Canvas public function open_object() { $this->_pdf->suspend_page(""); - $ret = $this->_pdf->begin_template($this->_width, $this->_height); + if ($this->getPDFLibMajorVersion() >= 7) { + $ret = $this->_pdf->begin_template_ext($this->_width, $this->_height, null); + } else { + $ret = $this->_pdf->begin_template($this->_width, $this->_height); + } $this->_pdf->save(); - $this->_objs[$ret] = array("start_page" => $this->_page_number); + $this->_objs[$ret] = ["start_page" => $this->_page_number]; return $ret; } @@ -367,7 +348,11 @@ class PDFLib implements Canvas public function close_object() { $this->_pdf->restore(); - $this->_pdf->end_template(); + if ($this->getPDFLibMajorVersion() >= 7) { + $this->_pdf->end_template_ext($this->_width, $this->_height); + } else { + $this->_pdf->end_template(); + } $this->_pdf->resume_page("pagenumber=" . $this->_page_number); } @@ -450,36 +435,23 @@ class PDFLib implements Canvas $this->_pdf->fit_image($obj, 0, 0, ""); } } - } - /** - * @return float|mixed - */ public function get_width() { return $this->_width; } - /** - * @return float|mixed - */ public function get_height() { return $this->_height; } - /** - * @return int - */ public function get_page_number() { return $this->_page_number; } - /** - * @return int - */ public function get_page_count() { return $this->_page_count; @@ -493,9 +465,6 @@ class PDFLib implements Canvas $this->_page_number = (int)$num; } - /** - * @param int $count - */ public function set_page_count($count) { $this->_page_count = (int)$count; @@ -505,15 +474,25 @@ class PDFLib implements Canvas * Sets the line style * * @param float $width - * @param $cap + * @param string $cap * @param string $join * @param array $dash - * - * @return void */ protected function _set_line_style($width, $cap, $join, $dash) { - if (count($dash) == 1) { + if (!is_array($dash)) { + $dash = []; + } + + // Work around PDFLib limitation with 0 dash length: + // Value 0 for option 'dasharray' is too small (minimum 1.5e-05) + foreach ($dash as &$d) { + if ($d == 0) { + $d = 1.5e-5; + } + } + + if (count($dash) === 1) { $dash[] = $dash[0]; } @@ -599,7 +578,11 @@ class PDFLib implements Canvas */ protected function _set_stroke_color($color) { + // TODO: we should check the current PDF stroke color + // instead of the cached value if ($this->_last_stroke_color == $color) { + // FIXME: do nothing, this optimization is broken by the + // stroke being set as a side effect of other operations //return; } @@ -612,16 +595,16 @@ class PDFLib implements Canvas if (isset($color[3])) { $type = "cmyk"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]); + list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], $color[3]]; } elseif (isset($color[2])) { $type = "rgb"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null); + list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], null]; } else { $type = "gray"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null); + list($c1, $c2, $c3, $c4) = [$color[0], $color[1], null, null]; } - $this->_set_stroke_opacity($alpha); + $this->_set_stroke_opacity($alpha, "Normal"); $this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4); } @@ -632,8 +615,12 @@ class PDFLib implements Canvas */ protected function _set_fill_color($color) { + // TODO: we should check the current PDF fill color + // instead of the cached value if ($this->_last_fill_color == $color) { - return; + // FIXME: do nothing, this optimization is broken by the + // fill being set as a side effect of other operations + //return; } $alpha = isset($color["alpha"]) ? $color["alpha"] : 1; @@ -645,28 +632,28 @@ class PDFLib implements Canvas if (isset($color[3])) { $type = "cmyk"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]); + list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], $color[3]]; } elseif (isset($color[2])) { $type = "rgb"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null); + list($c1, $c2, $c3, $c4) = [$color[0], $color[1], $color[2], null]; } else { $type = "gray"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null); + list($c1, $c2, $c3, $c4) = [$color[0], $color[1], null, null]; } - $this->_set_fill_opacity($alpha); + $this->_set_fill_opacity($alpha, "Normal"); $this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4); } /** * Sets the fill opacity * - * @param $opacity - * @param $mode + * @param float $opacity + * @param string $mode */ public function _set_fill_opacity($opacity, $mode = "Normal") { - if ($mode === "Normal" && is_null($opacity) === false) { + if ($mode === "Normal" && isset($opacity)) { $this->_set_gstate("opacityfill=$opacity"); } } @@ -674,25 +661,19 @@ class PDFLib implements Canvas /** * Sets the stroke opacity * - * @param $opacity - * @param $mode + * @param float $opacity + * @param string $mode */ public function _set_stroke_opacity($opacity, $mode = "Normal") { - if ($mode === "Normal" && is_null($opacity) === false) { + if ($mode === "Normal" && isset($opacity)) { $this->_set_gstate("opacitystroke=$opacity"); } } - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - public function set_opacity($opacity, $mode = "Normal") + public function set_opacity(float $opacity, string $mode = "Normal"): void { - if ($mode === "Normal" && is_null($opacity) === false) { + if ($mode === "Normal") { $this->_set_gstate("opacityfill=$opacity opacitystroke=$opacity"); $this->_current_opacity = $opacity; } @@ -714,7 +695,7 @@ class PDFLib implements Canvas return $this->_pdf->set_gstate($gstate); } - public function set_default_view($view, $options = array()) + public function set_default_view($view, $options = []) { // TODO // http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf @@ -760,6 +741,8 @@ class PDFLib implements Canvas $options .= " embedding=true"; } + $options .= " autosubsetting=" . ($this->_dompdf->getOptions()->getIsFontSubsettingEnabled() === false ? "false" : "true"); + if (is_null($encoding)) { // Unicode encoding is only available for the commerical // version of PDFlib and not PDFlib-Lite @@ -849,18 +832,9 @@ class PDFLib implements Canvas return $this->_height - $y; } - /** - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - public function line($x1, $y1, $x2, $y2, $color, $width, $style = null) + public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt") { - $this->_set_line_style($width, "butt", "", $style); + $this->_set_line_style($width, $cap, "", $style); $this->_set_stroke_color($color); $y1 = $this->y($y1); @@ -873,62 +847,23 @@ class PDFLib implements Canvas $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } - /** - * Draw line at the specified coordinates on every page. - * - * See {@link Style::munge_color()} for the format of the colour array. - * - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style optional - */ - public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = array()) + public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt") { - $_t = 'line'; - $this->_page_text[] = compact('_t', 'x1', 'y1', 'x2', 'y2', 'color', 'width', 'style'); - } - - /** - * @param float $x1 - * @param float $y1 - * @param float $r1 - * @param float $r2 - * @param float $astart - * @param float $aend - * @param array $color - * @param float $width - * @param array $style - */ - public function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) - { - $this->_set_line_style($width, "butt", "", $style); + $this->_set_line_style($width, $cap, "", $style); $this->_set_stroke_color($color); - $y1 = $this->y($y1); + $y = $this->y($y); - $this->_pdf->arc($x1, $y1, $r1, $astart, $aend); + $this->_pdf->arc($x, $y, $r1, $astart, $aend); $this->_pdf->stroke(); $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } - /** - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - * @param float $width - * @param null $style - */ - public function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) + public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt") { $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); + $this->_set_line_style($width, $cap, "", $style); $y1 = $this->y($y1) - $h; @@ -938,13 +873,6 @@ class PDFLib implements Canvas $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } - /** - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - */ public function filled_rectangle($x1, $y1, $w, $h, $color) { $this->_set_fill_color($color); @@ -957,12 +885,6 @@ class PDFLib implements Canvas $this->_set_fill_opacity($this->_current_opacity, "Normal"); } - /** - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - */ public function clipping_rectangle($x1, $y1, $w, $h) { $this->_pdf->save(); @@ -973,32 +895,72 @@ class PDFLib implements Canvas $this->_pdf->clip(); } - /** - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param float $rTL - * @param float $rTR - * @param float $rBR - * @param float $rBL - */ public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - $this->clipping_rectangle($x1, $y1, $w, $h); + if ($this->getPDFLibMajorVersion() < 9) { + //TODO: add PDFLib7 support + $this->clipping_rectangle($x1, $y1, $w, $h); + return; + } + + $this->_pdf->save(); + + // we use 0,0 for the base coordinates for the path points + // since we're drawing the path at the $x1,$y1 coordinates + + $path = 0; + //start: left edge, top end + $path = $this->_pdf->add_path_point($path, 0, 0 - $rTL + $h, "move", ""); + // line: left edge, bottom end + $path = $this->_pdf->add_path_point($path, 0, 0 + $rBL, "line", ""); + // curve: bottom-left corner + if ($rBL > 0) { + $path = $this->_pdf->add_path_point($path, 0 + $rBL, 0, "elliptical", "radius=$rBL clockwise=false"); + } + // line: bottom edge, left end + $path = $this->_pdf->add_path_point($path, 0 - $rBR + $w, 0, "line", ""); + // curve: bottom-right corner + if ($rBR > 0) { + $path = $this->_pdf->add_path_point($path, 0 + $w, 0 + $rBR, "elliptical", "radius=$rBR clockwise=false"); + } + // line: right edge, top end + $path = $this->_pdf->add_path_point($path, 0 + $w, 0 - $rTR + $h, "line", ""); + // curve: top-right corner + if ($rTR > 0) { + $path = $this->_pdf->add_path_point($path, 0 - $rTR + $w, 0 + $h, "elliptical", "radius=$rTR clockwise=false"); + } + // line: top edge, left end + $path = $this->_pdf->add_path_point($path, 0 + $rTL, 0 + $h, "line", ""); + // curve: top-left corner + if ($rTL > 0) { + $path = $this->_pdf->add_path_point($path, 0, 0 - $rTL + $h, "elliptical", "radius=$rTL clockwise=false"); + } + $this->_pdf->draw_path($path, $x1, $this->_height-$y1-$h, "clip=true"); + } + + public function clipping_polygon(array $points): void + { + $this->_pdf->save(); + + $y = $this->y(array_pop($points)); + $x = array_pop($points); + $this->_pdf->moveto($x, $y); + + while (count($points) > 1) { + $y = $this->y(array_pop($points)); + $x = array_pop($points); + $this->_pdf->lineto($x, $y); + } + + $this->_pdf->closepath(); + $this->_pdf->clip(); } - /** - * - */ public function clipping_end() { $this->_pdf->restore(); } - /** - * - */ public function save() { $this->_pdf->save(); @@ -1009,11 +971,6 @@ class PDFLib implements Canvas $this->_pdf->restore(); } - /** - * @param $angle - * @param $x - * @param $y - */ public function rotate($angle, $x, $y) { $pdf = $this->_pdf; @@ -1022,12 +979,6 @@ class PDFLib implements Canvas $pdf->translate(-$x, -$this->_height + $y); } - /** - * @param $angle_x - * @param $angle_y - * @param $x - * @param $y - */ public function skew($angle_x, $angle_y, $x, $y) { $pdf = $this->_pdf; @@ -1036,12 +987,6 @@ class PDFLib implements Canvas $pdf->translate(-$x, -$this->_height + $y); } - /** - * @param $s_x - * @param $s_y - * @param $x - * @param $y - */ public function scale($s_x, $s_y, $x, $y) { $pdf = $this->_pdf; @@ -1050,36 +995,17 @@ class PDFLib implements Canvas $pdf->translate(-$x, -$this->_height + $y); } - /** - * @param $t_x - * @param $t_y - */ public function translate($t_x, $t_y) { $this->_pdf->translate($t_x, -$t_y); } - /** - * @param $a - * @param $b - * @param $c - * @param $d - * @param $e - * @param $f - */ public function transform($a, $b, $c, $d, $e, $f) { $this->_pdf->concat($a, $b, $c, $d, $e, $f); } - /** - * @param array $points - * @param array $color - * @param null $width - * @param null $style - * @param bool $fill - */ - public function polygon($points, $color, $width = null, $style = null, $fill = false) + public function polygon($points, $color, $width = null, $style = [], $fill = false) { $this->_set_fill_color($color); $this->_set_stroke_color($color); @@ -1108,16 +1034,7 @@ class PDFLib implements Canvas $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } - /** - * @param float $x - * @param float $y - * @param float $r - * @param array $color - * @param null $width - * @param null $style - * @param bool $fill - */ - public function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) + public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false) { $this->_set_fill_color($color); $this->_set_stroke_color($color); @@ -1140,44 +1057,49 @@ class PDFLib implements Canvas $this->_set_stroke_opacity($this->_current_opacity, "Normal"); } - /** - * @param string $img_url - * @param float $x - * @param float $y - * @param int $w - * @param int $h - * @param string $resolution - */ - public function image($img_url, $x, $y, $w, $h, $resolution = "normal") + public function image($img, $x, $y, $w, $h, $resolution = "normal") { $w = (int)$w; $h = (int)$h; - $img_type = Cache::detect_type($img_url, $this->get_dompdf()->getHttpContext()); + $img_type = Cache::detect_type($img, $this->get_dompdf()->getHttpContext()); - if (!isset($this->_imgs[$img_url])) { - $this->_imgs[$img_url] = $this->_pdf->load_image($img_type, $img_url, ""); + // Strip file:// prefix + if (substr($img, 0, 7) === "file://") { + $img = substr($img, 7); } - $img = $this->_imgs[$img_url]; + if (!isset($this->_imgs[$img])) { + if (strtolower($img_type) === "svg") { + //FIXME: PDFLib loads SVG but returns error message "Function must not be called in 'page' scope" + $image_load_response = $this->_pdf->load_graphics($img_type, $img, ""); + } else { + $image_load_response = $this->_pdf->load_image($img_type, $img, ""); + } + if ($image_load_response === 0) { + //TODO: should do something with the error message + $error = $this->_pdf->get_errmsg(); + return; + } + $this->_imgs[$img] = $image_load_response; + } + + $img = $this->_imgs[$img]; $y = $this->y($y) - $h; - $this->_pdf->fit_image($img, $x, $y, 'boxsize={' . "$w $h" . '} fitmethod=entire'); + if (strtolower($img_type) === "svg") { + $this->_pdf->fit_graphics($img, $x, $y, 'boxsize={' . "$w $h" . '} fitmethod=entire'); + } else { + $this->_pdf->fit_image($img, $x, $y, 'boxsize={' . "$w $h" . '} fitmethod=entire'); + } } - /** - * @param float $x - * @param float $y - * @param string $text - * @param string $font - * @param float $size - * @param array $color - * @param int $word_spacing - * @param int $char_spacing - * @param int $angle - */ - public function text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_spacing = 0, $char_spacing = 0, $angle = 0) + public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_spacing = 0, $char_spacing = 0, $angle = 0) { + if ($size == 0) { + return; + } + $fh = $this->_load_font($font); $this->_pdf->setfont($fh, $size); @@ -1194,9 +1116,6 @@ class PDFLib implements Canvas $this->_set_fill_opacity($this->_current_opacity, "Normal"); } - /** - * @param string $code - */ public function javascript($code) { if (strlen($this->_dompdf->getOptions()->getPdflibLicense()) > 0) { @@ -1204,25 +1123,11 @@ class PDFLib implements Canvas } } - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ public function add_named_dest($anchorname) { $this->_pdf->add_nameddest($anchorname, ""); } - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ public function add_link($url, $x, $y, $width, $height) { $y = $this->y($y) - $height; @@ -1234,29 +1139,21 @@ class PDFLib implements Canvas "contents={$url} destname=" . substr($url, 1) . " linewidth=0"); } } else { - list($proto, $host, $path, $file) = Helpers::explode_url($url); - - if ($proto == "" || $proto === "file://") { - return; // Local links are not allowed + //TODO: PDFLib::create_action does not permit non-HTTP links for URI actions + $action = $this->_pdf->create_action("URI", "url={{$url}}"); + // add the annotation only if the action was created + if ($action !== 0) { + $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={{$url}} action={activate=$action} linewidth=0"); } - $url = Helpers::build_url($proto, $host, $path, $file); - $url = '{' . rawurldecode($url) . '}'; - - $action = $this->_pdf->create_action("URI", "url=" . $url); - $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0"); } } - /** - * @param string $text - * @param string $font - * @param float $size - * @param int $word_spacing - * @param int $letter_spacing - * @return mixed - */ - public function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0) + public function get_text_width($text, $font, $size, $word_spacing = 0.0, $letter_spacing = 0.0) { + if ($size == 0) { + return 0.0; + } + $fh = $this->_load_font($font); // Determine the additional width due to extra spacing @@ -1265,37 +1162,31 @@ class PDFLib implements Canvas if ($letter_spacing) { $num_chars = mb_strlen($text); - $delta += ($num_chars - $num_spaces) * $letter_spacing; + $delta += $num_chars * $letter_spacing; } return $this->_pdf->stringwidth($text, $fh, $size) + $delta; } - /** - * @param string $font - * @param float $size - * @return float - */ public function get_font_height($font, $size) { + if ($size == 0) { + return 0.0; + } + $fh = $this->_load_font($font); $this->_pdf->setfont($fh, $size); - $asc = $this->_pdf->get_value("ascender", $fh); - $desc = $this->_pdf->get_value("descender", $fh); + $asc = $this->_pdf->info_font($fh, "ascender", "fontsize=$size"); + $desc = $this->_pdf->info_font($fh, "descender", "fontsize=$size"); // $desc is usually < 0, $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); - return $size * ($asc - $desc) * $ratio; + return (abs($asc) + abs($desc)) * $ratio; } - /** - * @param string $font - * @param float $size - * @return float - */ public function get_font_baseline($font, $size) { $ratio = $this->_dompdf->getOptions()->getFontHeightRatio(); @@ -1304,51 +1195,55 @@ class PDFLib implements Canvas } /** - * Writes text at the specified x and y coordinates on every page + * Processes a callback or script on every page. * - * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced - * with their current values. + * The callback function receives the four parameters `int $pageNumber`, + * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in + * that order. If a script is passed as string, the variables `$PAGE_NUM`, + * `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing + * a script as string is deprecated and will be removed in a future version. * - * See {@link Style::munge_color()} for the format of the color array. + * This function can be used to add page numbers to all pages after the + * first one, for example. * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle to write the text at, measured CW starting from the x-axis + * @param callable|string $callback The callback function or PHP script to process on every page */ - public function page_text($x, $y, $text, $font, $size, $color = array(0, 0, 0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) + public function page_script($callback): void { - $_t = "text"; - $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle"); + if (is_string($callback)) { + $this->processPageScript(function ( + int $PAGE_NUM, + int $PAGE_COUNT, + self $pdf, + FontMetrics $fontMetrics + ) use ($callback) { + eval($callback); + }); + return; + } + + $this->processPageScript($callback); } - //........................................................................ - - /** - * Processes a script on every page - * - * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available. - * - * This function can be used to add page numbers to all pages - * after the first one, for example. - * - * @param string $code the script code - * @param string $type the language type for script - */ - public function page_script($code, $type = "text/php") + public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $_t = "script"; - $this->_page_text[] = compact("_t", "code", "type"); + $this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) { + $text = str_replace( + ["{PAGE_NUM}", "{PAGE_COUNT}"], + [$pageNumber, $pageCount], + $text + ); + $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); + }); + } + + public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = []) + { + $this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) { + $this->line($x1, $y1, $x2, $y2, $color, $width, $style); + }); } - /** - * - */ public function new_page() { // Add objects to the current page @@ -1359,44 +1254,15 @@ class PDFLib implements Canvas $this->_page_number = ++$this->_page_count; } - /** - * Add text to each page after rendering is complete - */ - protected function _add_page_text() + protected function processPageScript(callable $callback): void { - if (!count($this->_page_text)) { - return; - } - - $eval = null; $this->_pdf->suspend_page(""); for ($p = 1; $p <= $this->_page_count; $p++) { $this->_pdf->resume_page("pagenumber=$p"); - foreach ($this->_page_text as $pt) { - extract($pt); - - switch ($_t) { - case "text": - $text = str_replace(array("{PAGE_NUM}", "{PAGE_COUNT}"), - array($p, $this->_page_count), $text); - $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - break; - - case "script": - if (!$eval) { - $eval = new PHPEvaluator($this); - } - $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count)); - break; - - case 'line': - $this->line( $x1, $y1, $x2, $y2, $color, $width, $style ); - break; - - } - } + $fontMetrics = $this->_dompdf->getFontMetrics(); + $callback($p, $this->_page_count, $this, $fontMetrics); $this->_pdf->suspend_page(""); } @@ -1405,13 +1271,9 @@ class PDFLib implements Canvas } /** - * Streams the PDF to the client. - * - * @param string $filename The filename to present to the client. - * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). * @throws Exception */ - public function stream($filename = "document.pdf", $options = array()) + public function stream($filename = "document.pdf", $options = []) { if (headers_sent()) { die("Unable to stream pdf: headers already sent"); @@ -1424,8 +1286,6 @@ class PDFLib implements Canvas $options["Attachment"] = true; } - $this->_add_page_text(); - if ($options["compress"]) { $this->setPDFLibValue("compress", 6); } else { @@ -1447,7 +1307,7 @@ class PDFLib implements Canvas header("Content-Type: application/pdf"); header("Content-Length: " . $size); - $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf"; + $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf"; $attachment = $options["Attachment"] ? "attachment" : "inline"; header(Helpers::buildContentDispositionHeader($attachment, $filename)); @@ -1480,20 +1340,12 @@ class PDFLib implements Canvas flush(); } - /** - * Returns the PDF as a string. - * - * @param array $options Associative array: 'compress' => 1 or 0 (default 1). - * @return string - */ - public function output($options = array()) + public function output($options = []) { if (!isset($options["compress"])) { $options["compress"] = true; } - $this->_add_page_text(); - if ($options["compress"]) { $this->setPDFLibValue("compress", 6); } else { @@ -1580,7 +1432,7 @@ class PDFLib implements Canvas /** * @return int */ - private function getPDFLibMajorVersion() + protected function getPDFLibMajorVersion() { if (is_null(self::$MAJOR_VERSION)) { if (method_exists($this->_pdf, "get_option")) { diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Canvas.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Canvas.php new file mode 100644 index 000000000..1812def58 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Canvas.php @@ -0,0 +1,477 @@ + alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $width + * @param array $style + * @param string $cap `butt`, `round`, or `square` + */ + function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt"); + + /** + * Draws an arc + * + * See {@link Cpdf::setLineStyle()} for a description of the format of the + * $style and $cap parameters (aka dash and cap). + * + * @param float $x X coordinate of the arc + * @param float $y Y coordinate of the arc + * @param float $r1 Radius 1 + * @param float $r2 Radius 2 + * @param float $astart Start angle in degrees + * @param float $aend End angle in degrees + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $width + * @param array $style + * @param string $cap `butt`, `round`, or `square` + */ + function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt"); + + /** + * Draws a rectangle at x1,y1 with width w and height h + * + * See {@link Cpdf::setLineStyle()} for a description of the format of the + * $style and $cap parameters (aka dash and cap). + * + * @param float $x1 + * @param float $y1 + * @param float $w + * @param float $h + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $width + * @param array $style + * @param string $cap `butt`, `round`, or `square` + */ + function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt"); + + /** + * Draws a filled rectangle at x1,y1 with width w and height h + * + * @param float $x1 + * @param float $y1 + * @param float $w + * @param float $h + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + */ + function filled_rectangle($x1, $y1, $w, $h, $color); + + /** + * Starts a clipping rectangle at x1,y1 with width w and height h + * + * @param float $x1 + * @param float $y1 + * @param float $w + * @param float $h + */ + function clipping_rectangle($x1, $y1, $w, $h); + + /** + * Starts a rounded clipping rectangle at x1,y1 with width w and height h + * + * @param float $x1 + * @param float $y1 + * @param float $w + * @param float $h + * @param float $tl + * @param float $tr + * @param float $br + * @param float $bl + */ + function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl); + + /** + * Starts a clipping polygon + * + * @param float[] $points + */ + public function clipping_polygon(array $points): void; + + /** + * Ends the last clipping shape + */ + function clipping_end(); + + /** + * Processes a callback on every page. + * + * The callback function receives the four parameters `int $pageNumber`, + * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in + * that order. + * + * This function can be used to add page numbers to all pages after the + * first one, for example. + * + * @param callable $callback The callback function to process on every page + */ + public function page_script($callback): void; + + /** + * Writes text at the specified x and y coordinates on every page. + * + * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced + * with their current values. + * + * @param float $x + * @param float $y + * @param string $text The text to write + * @param string $font The font file to use + * @param float $size The font size, in points + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $word_space Word spacing adjustment + * @param float $char_space Char spacing adjustment + * @param float $angle Angle to write the text at, measured clockwise starting from the x-axis + */ + public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0); + + /** + * Draws a line at the specified coordinates on every page. + * + * @param float $x1 + * @param float $y1 + * @param float $x2 + * @param float $y2 + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $width + * @param array $style + */ + public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = []); + + /** + * Save current state + */ + function save(); + + /** + * Restore last state + */ + function restore(); + + /** + * Rotate + * + * @param float $angle angle in degrees for counter-clockwise rotation + * @param float $x Origin abscissa + * @param float $y Origin ordinate + */ + function rotate($angle, $x, $y); + + /** + * Skew + * + * @param float $angle_x + * @param float $angle_y + * @param float $x Origin abscissa + * @param float $y Origin ordinate + */ + function skew($angle_x, $angle_y, $x, $y); + + /** + * Scale + * + * @param float $s_x scaling factor for width as percent + * @param float $s_y scaling factor for height as percent + * @param float $x Origin abscissa + * @param float $y Origin ordinate + */ + function scale($s_x, $s_y, $x, $y); + + /** + * Translate + * + * @param float $t_x movement to the right + * @param float $t_y movement to the bottom + */ + function translate($t_x, $t_y); + + /** + * Transform + * + * @param float $a + * @param float $b + * @param float $c + * @param float $d + * @param float $e + * @param float $f + */ + function transform($a, $b, $c, $d, $e, $f); + + /** + * Draws a polygon + * + * The polygon is formed by joining all the points stored in the $points + * array. $points has the following structure: + * ``` + * array(0 => x1, + * 1 => y1, + * 2 => x2, + * 3 => y2, + * ... + * ); + * ``` + * + * See {@link Cpdf::setLineStyle()} for a description of the format of the + * $style parameter (aka dash). + * + * @param array $points + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $width + * @param array $style + * @param bool $fill Fills the polygon if true + */ + function polygon($points, $color, $width = null, $style = [], $fill = false); + + /** + * Draws a circle at $x,$y with radius $r + * + * See {@link Cpdf::setLineStyle()} for a description of the format of the + * $style parameter (aka dash). + * + * @param float $x + * @param float $y + * @param float $r + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $width + * @param array $style + * @param bool $fill Fills the circle if true + */ + function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false); + + /** + * Add an image to the pdf. + * + * The image is placed at the specified x and y coordinates with the + * given width and height. + * + * @param string $img The path to the image + * @param float $x X position + * @param float $y Y position + * @param float $w Width + * @param float $h Height + * @param string $resolution The resolution of the image + */ + function image($img, $x, $y, $w, $h, $resolution = "normal"); + + /** + * Writes text at the specified x and y coordinates + * + * @param float $x + * @param float $y + * @param string $text The text to write + * @param string $font The font file to use + * @param float $size The font size, in points + * @param array $color Color array in the format `[r, g, b, "alpha" => alpha]` + * where r, g, b, and alpha are float values between 0 and 1 + * @param float $word_space Word spacing adjustment + * @param float $char_space Char spacing adjustment + * @param float $angle Angle to write the text at, measured clockwise starting from the x-axis + */ + function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0); + + /** + * Add a named destination (similar to ... in html) + * + * @param string $anchorname The name of the named destination + */ + function add_named_dest($anchorname); + + /** + * Add a link to the pdf + * + * @param string $url The url to link to + * @param float $x The x position of the link + * @param float $y The y position of the link + * @param float $width The width of the link + * @param float $height The height of the link + */ + function add_link($url, $x, $y, $width, $height); + + /** + * Add meta information to the PDF. + * + * @param string $label Label of the value (Creator, Producer, etc.) + * @param string $value The text to set + */ + public function add_info(string $label, string $value): void; + + /** + * Calculates text size, in points + * + * @param string $text The text to be sized + * @param string $font The font file to use + * @param float $size The font size, in points + * @param float $word_spacing Word spacing, if any + * @param float $char_spacing Char spacing, if any + * + * @return float + */ + function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0); + + /** + * Calculates font height, in points + * + * @param string $font The font file to use + * @param float $size The font size, in points + * + * @return float + */ + function get_font_height($font, $size); + + /** + * Returns the font x-height, in points + * + * @param string $font The font file to use + * @param float $size The font size, in points + * + * @return float + */ + //function get_font_x_height($font, $size); + + /** + * Calculates font baseline, in points + * + * @param string $font The font file to use + * @param float $size The font size, in points + * + * @return float + */ + function get_font_baseline($font, $size); + + /** + * Returns the PDF's width in points + * + * @return float + */ + function get_width(); + + /** + * Returns the PDF's height in points + * + * @return float + */ + function get_height(); + + /** + * Sets the opacity + * + * @param float $opacity + * @param string $mode + */ + public function set_opacity(float $opacity, string $mode = "Normal"): void; + + /** + * Sets the default view + * + * @param string $view + * 'XYZ' left, top, zoom + * 'Fit' + * 'FitH' top + * 'FitV' left + * 'FitR' left,bottom,right + * 'FitB' + * 'FitBH' top + * 'FitBV' left + * @param array $options + */ + function set_default_view($view, $options = []); + + /** + * @param string $code + */ + function javascript($code); + + /** + * Starts a new page + * + * Subsequent drawing operations will appear on the new page. + */ + function new_page(); + + /** + * Streams the PDF to the client. + * + * @param string $filename The filename to present to the client. + * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). + */ + function stream($filename, $options = []); + + /** + * Returns the PDF as a string. + * + * @param array $options Associative array: 'compress' => 1 or 0 (default 1). + * + * @return string + */ + function output($options = []); +} diff --git a/library/vendor/dompdf/src/CanvasFactory.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/CanvasFactory.php similarity index 93% rename from library/vendor/dompdf/src/CanvasFactory.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/CanvasFactory.php index b2bf1276a..86352e1dc 100644 --- a/library/vendor/dompdf/src/CanvasFactory.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/CanvasFactory.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf; diff --git a/library/vendor/dompdf/src/Cellmap.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Cellmap.php similarity index 61% rename from library/vendor/dompdf/src/Cellmap.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Cellmap.php index e426ef671..e6c1c68e6 100644 --- a/library/vendor/dompdf/src/Cellmap.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Cellmap.php @@ -1,12 +1,12 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf; +use Dompdf\FrameDecorator\AbstractFrameDecorator; use Dompdf\FrameDecorator\Table as TableFrameDecorator; use Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator; @@ -22,21 +22,18 @@ class Cellmap { /** * Border style weight lookup for collapsed border resolution. - * - * @var array */ - protected static $_BORDER_STYLE_SCORE = array( - "inset" => 1, - "groove" => 2, - "outset" => 3, - "ridge" => 4, - "dotted" => 5, - "dashed" => 6, - "solid" => 7, + protected const BORDER_STYLE_SCORE = [ "double" => 8, - "hidden" => 9, - "none" => 0, - ); + "solid" => 7, + "dashed" => 6, + "dotted" => 5, + "ridge" => 4, + "outset" => 3, + "groove" => 2, + "inset" => 1, + "none" => 0 + ]; /** * The table object this cellmap is attached to. @@ -90,7 +87,7 @@ class Cellmap /** * 1D Array mapping frames to (multiple) pairs, keyed on frame_id. * - * @var Frame[] + * @var array[] */ protected $_frames; @@ -109,14 +106,14 @@ class Cellmap private $__row; /** - * Tells wether the columns' width can be modified + * Tells whether the columns' width can be modified * * @var bool */ private $_columns_locked = false; /** - * Tells wether the table has table-layout:fixed + * Tells whether the table has table-layout:fixed * * @var bool */ @@ -131,32 +128,26 @@ class Cellmap $this->reset(); } - /** - * - */ - public function reset() + public function reset(): void { $this->_num_rows = 0; $this->_num_cols = 0; - $this->_cells = array(); - $this->_frames = array(); + $this->_cells = []; + $this->_frames = []; if (!$this->_columns_locked) { - $this->_columns = array(); + $this->_columns = []; } - $this->_rows = array(); + $this->_rows = []; - $this->_borders = array(); + $this->_borders = []; $this->__col = $this->__row = 0; } - /** - * - */ - public function lock_columns() + public function lock_columns(): void { $this->_columns_locked = true; } @@ -170,9 +161,9 @@ class Cellmap } /** - * @param $fixed + * @param bool $fixed */ - public function set_layout_fixed($fixed) + public function set_layout_fixed(bool $fixed) { $this->_fixed_layout = $fixed; } @@ -225,7 +216,7 @@ class Cellmap public function &get_column($i) { if (!isset($this->_columns[$i])) { - $this->_columns[$i] = array( + $this->_columns[$i] = [ "x" => 0, "min-width" => 0, "max-width" => 0, @@ -233,7 +224,7 @@ class Cellmap "absolute" => 0, "percent" => 0, "auto" => true, - ); + ]; } return $this->_columns[$i]; @@ -255,11 +246,11 @@ class Cellmap public function &get_row($j) { if (!isset($this->_rows[$j])) { - $this->_rows[$j] = array( + $this->_rows[$j] = [ "y" => 0, "first-column" => 0, "height" => null, - ); + ]; } return $this->_rows[$j]; @@ -276,11 +267,11 @@ class Cellmap public function get_border($i, $j, $h_v, $prop = null) { if (!isset($this->_borders[$i][$j][$h_v])) { - $this->_borders[$i][$j][$h_v] = array( + $this->_borders[$i][$j][$h_v] = [ "width" => 0, "style" => "solid", "color" => "black", - ); + ]; } if (isset($prop)) { @@ -298,18 +289,18 @@ class Cellmap */ public function get_border_properties($i, $j) { - return array( + return [ "top" => $this->get_border($i, $j, "horizontal"), "right" => $this->get_border($i, $j + 1, "vertical"), "bottom" => $this->get_border($i + 1, $j, "horizontal"), "left" => $this->get_border($i, $j, "vertical"), - ); + ]; } /** * @param Frame $frame * - * @return null|Frame + * @return array|null */ public function get_spanned_cells(Frame $frame) { @@ -350,24 +341,26 @@ class Cellmap throw new Exception("Frame not found in cellmap"); } + // Positions are stored relative to the table position + [$table_x, $table_y] = $this->_table->get_position(); $col = $this->_frames[$key]["columns"][0]; $row = $this->_frames[$key]["rows"][0]; if (!isset($this->_columns[$col])) { $_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs."; - $x = 0; + $x = $table_x; } else { - $x = $this->_columns[$col]["x"]; + $x = $table_x + $this->_columns[$col]["x"]; } if (!isset($this->_rows[$row])) { $_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs."; - $y = 0; + $y = $table_y; } else { - $y = $this->_rows[$row]["y"]; + $y = $table_y + $this->_rows[$row]["y"]; } - return array($x, $y, "x" => $x, "y" => $y); + return [$x, $y, "x" => $x, "y" => $y]; } /** @@ -434,89 +427,130 @@ class Cellmap $col =& $this->get_column($j); $col["used-width"] = $width; $next_col =& $this->get_column($j + 1); - $next_col["x"] = $next_col["x"] + $width; + $next_col["x"] = $col["x"] + $width; } /** * @param int $i - * @param mixed $height + * @param long $height */ public function set_row_height($i, $height) { $row =& $this->get_row($i); + if ($height > $row["height"]) { + $row["height"] = $height; + } + $next_row =& $this->get_row($i + 1); + $next_row["y"] = $row["y"] + $row["height"]; + } - if ($row["height"] !== null && $height <= $row["height"]) { + /** + * https://www.w3.org/TR/CSS21/tables.html#border-conflict-resolution + * + * @param int $i + * @param int $j + * @param string $h_v `horizontal` or `vertical` + * @param array $border_spec + */ + protected function resolve_border(int $i, int $j, string $h_v, array $border_spec): void + { + if (!isset($this->_borders[$i][$j][$h_v])) { + $this->_borders[$i][$j][$h_v] = $border_spec; return; } - $row["height"] = $height; - $next_row =& $this->get_row($i + 1); - $next_row["y"] = $row["y"] + $height; + $border = $this->_borders[$i][$j][$h_v]; - } - - /** - * @param int $i - * @param int $j - * @param mixed $h_v - * @param mixed $border_spec - * - * @return mixed - */ - protected function _resolve_border($i, $j, $h_v, $border_spec) - { $n_width = $border_spec["width"]; $n_style = $border_spec["style"]; - - if (!isset($this->_borders[$i][$j][$h_v])) { - $this->_borders[$i][$j][$h_v] = $border_spec; - - return $this->_borders[$i][$j][$h_v]["width"]; - } - - $border = & $this->_borders[$i][$j][$h_v]; - $o_width = $border["width"]; $o_style = $border["style"]; - if (($n_style === "hidden" || - $n_width > $o_width || - $o_style === "none") - - or - - ($o_width == $n_width && - in_array($n_style, self::$_BORDER_STYLE_SCORE) && - self::$_BORDER_STYLE_SCORE[$n_style] > self::$_BORDER_STYLE_SCORE[$o_style]) - ) { - $border = $border_spec; + if ($o_style === "hidden") { + return; } - return $border["width"]; + // A style of `none` has lowest priority independent of its specified + // width here, as its resolved width is always 0 + if ($n_style === "hidden" || $n_width > $o_width + || ($o_width == $n_width + && isset(self::BORDER_STYLE_SCORE[$n_style]) + && isset(self::BORDER_STYLE_SCORE[$o_style]) + && self::BORDER_STYLE_SCORE[$n_style] > self::BORDER_STYLE_SCORE[$o_style]) + ) { + $this->_borders[$i][$j][$h_v] = $border_spec; + } } /** - * @param Frame $frame + * Get the resolved border properties for the given frame. + * + * @param AbstractFrameDecorator $frame + * + * @return array[] */ - public function add_frame(Frame $frame) + protected function get_resolved_border(AbstractFrameDecorator $frame): array + { + $key = $frame->get_id(); + $columns = $this->_frames[$key]["columns"]; + $rows = $this->_frames[$key]["rows"]; + + $first_col = $columns[0]; + $last_col = $columns[count($columns) - 1]; + $first_row = $rows[0]; + $last_row = $rows[count($rows) - 1]; + + $max_top = null; + $max_bottom = null; + $max_left = null; + $max_right = null; + + foreach ($columns as $col) { + $top = $this->_borders[$first_row][$col]["horizontal"]; + $bottom = $this->_borders[$last_row + 1][$col]["horizontal"]; + + if ($max_top === null || $top["width"] > $max_top["width"]) { + $max_top = $top; + } + if ($max_bottom === null || $bottom["width"] > $max_bottom["width"]) { + $max_bottom = $bottom; + } + } + + foreach ($rows as $row) { + $left = $this->_borders[$row][$first_col]["vertical"]; + $right = $this->_borders[$row][$last_col + 1]["vertical"]; + + if ($max_left === null || $left["width"] > $max_left["width"]) { + $max_left = $left; + } + if ($max_right === null || $right["width"] > $max_right["width"]) { + $max_right = $right; + } + } + + return [$max_top, $max_right, $max_bottom, $max_left]; + } + + /** + * @param AbstractFrameDecorator $frame + */ + public function add_frame(Frame $frame): void { $style = $frame->get_style(); $display = $style->display; - $collapse = $this->_table->get_style()->border_collapse == "collapse"; + $collapse = $this->_table->get_style()->border_collapse === "collapse"; - // Recursively add the frames within tables, table-row-groups and table-rows - if ($display === "table-row" || - $display === "table" || - $display === "inline-table" || - in_array($display, TableFrameDecorator::$ROW_GROUPS) + // Recursively add the frames within the table, its row groups and rows + if ($frame === $this->_table + || $display === "table-row" + || in_array($display, TableFrameDecorator::ROW_GROUPS, true) ) { $start_row = $this->__row; + foreach ($frame->get_children() as $child) { - // Ignore all Text frames and :before/:after pseudo-selector elements. - if (!($child instanceof FrameDecorator\Text) && $child->get_node()->nodeName !== 'dompdf_generated') { - $this->add_frame($child); - } + $this->add_frame($child); } if ($display === "table-row") { @@ -531,45 +565,55 @@ class Cellmap $this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1)); $this->_frames[$key]["frame"] = $frame; - if ($display !== "table-row" && $collapse) { + if ($collapse) { $bp = $style->get_border_properties(); - // Resolve the borders + // Resolve vertical borders for ($i = 0; $i < $num_rows + 1; $i++) { - $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]); - $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]); + $this->resolve_border($start_row + $i, 0, "vertical", $bp["left"]); + $this->resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]); } + // Resolve horizontal borders for ($j = 0; $j < $this->_num_cols; $j++) { - $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]); - $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]); + $this->resolve_border($start_row, $j, "horizontal", $bp["top"]); + $this->resolve_border($this->__row, $j, "horizontal", $bp["bottom"]); } + + if ($frame === $this->_table) { + // Clear borders because the cells are now using them. The + // border width still needs to be set to half the resolved + // width so that the table is positioned properly + [$top, $right, $bottom, $left] = $this->get_resolved_border($frame); + + $style->set_used("border_top_width", $top["width"] / 2); + $style->set_used("border_right_width", $right["width"] / 2); + $style->set_used("border_bottom_width", $bottom["width"] / 2); + $style->set_used("border_left_width", $left["width"] / 2); + $style->set_used("border_style", "none"); + } else { + // Clear borders for rows and row groups + $style->set_used("border_width", 0); + $style->set_used("border_style", "none"); + } + } + + if ($frame === $this->_table) { + // Apply resolved borders to table cells and calculate column + // widths after all frames have been added + $this->calculate_column_widths(); } return; } - $node = $frame->get_node(); - - // Determine where this cell is going - $colspan = $node->getAttribute("colspan"); - $rowspan = $node->getAttribute("rowspan"); - - if (!$colspan) { - $colspan = 1; - $node->setAttribute("colspan", 1); - } - - if (!$rowspan) { - $rowspan = 1; - $node->setAttribute("rowspan", 1); - } + // Add the frame to the cellmap $key = $frame->get_id(); - + $node = $frame->get_node(); $bp = $style->get_border_properties(); - - // Add the frame to the cellmap - $max_left = $max_right = 0; + // Determine where this cell is going + $colspan = max((int) $node->getAttribute("colspan"), 1); + $rowspan = max((int) $node->getAttribute("rowspan"), 1); // Find the next available column (fix by Ciro Mondueri) $ac = $this->__col; @@ -591,13 +635,11 @@ class Cellmap if ($collapse) { // Resolve vertical borders - $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"])); - $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"])); + $this->resolve_border($row, $this->__col, "vertical", $bp["left"]); + $this->resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]); } } - $max_top = $max_bottom = 0; - // Columns: for ($j = 0; $j < $colspan; $j++) { $col = $this->__col + $j; @@ -605,42 +647,84 @@ class Cellmap if ($collapse) { // Resolve horizontal borders - $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"])); - $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"])); + $this->resolve_border($this->__row, $col, "horizontal", $bp["top"]); + $this->resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]); } } $this->_frames[$key]["frame"] = $frame; - // Handle seperated border model - if (!$collapse) { - list($h, $v) = $this->_table->get_style()->border_spacing; + $this->__col += $colspan; + if ($this->__col > $this->_num_cols) { + $this->_num_cols = $this->__col; + } + } - // Border spacing is effectively a margin between cells - $v = $style->length_in_pt($v); - if (is_numeric($v)) { - $v = $v / 2; - } - $h = $style->length_in_pt($h); - if (is_numeric($h)) { - $h = $h / 2; - } - $style->margin = "$v $h"; + /** + * Apply resolved borders to table cells and calculate column widths. + */ + protected function calculate_column_widths(): void + { + $table = $this->_table; + $table_style = $table->get_style(); + $collapse = $table_style->border_collapse === "collapse"; - // The additional 1/2 width gets added to the table proper + if ($collapse) { + $v_spacing = 0; + $h_spacing = 0; } else { - // Drop the frame's actual border - $style->border_left_width = $max_left / 2; - $style->border_right_width = $max_right / 2; - $style->border_top_width = $max_top / 2; - $style->border_bottom_width = $max_bottom / 2; - $style->margin = "none"; + // The additional 1/2 width gets added to the table proper + [$h, $v] = $table_style->border_spacing; + $v_spacing = $v / 2; + $h_spacing = $h / 2; } - if (!$this->_columns_locked) { + foreach ($this->_frames as $frame_info) { + /** @var TableCellFrameDecorator */ + $frame = $frame_info["frame"]; + $style = $frame->get_style(); + $display = $style->display; + + if ($display !== "table-cell") { + continue; + } + + if ($collapse) { + // Set the resolved border at half width + [$top, $right, $bottom, $left] = $this->get_resolved_border($frame); + + $style->set_used("border_top_width", $top["width"] / 2); + $style->set_used("border_top_style", $top["style"]); + $style->set_used("border_top_color", $top["color"]); + $style->set_used("border_right_width", $right["width"] / 2); + $style->set_used("border_right_style", $right["style"]); + $style->set_used("border_right_color", $right["color"]); + $style->set_used("border_bottom_width", $bottom["width"] / 2); + $style->set_used("border_bottom_style", $bottom["style"]); + $style->set_used("border_bottom_color", $bottom["color"]); + $style->set_used("border_left_width", $left["width"] / 2); + $style->set_used("border_left_style", $left["style"]); + $style->set_used("border_left_color", $left["color"]); + $style->set_used("margin", 0); + } else { + // Border spacing is effectively a margin between cells + $style->set_used("margin_top", $v_spacing); + $style->set_used("margin_bottom", $v_spacing); + $style->set_used("margin_left", $h_spacing); + $style->set_used("margin_right", $h_spacing); + } + + if ($this->_columns_locked) { + continue; + } + + $node = $frame->get_node(); + $colspan = max((int) $node->getAttribute("colspan"), 1); + $first_col = $frame_info["columns"][0]; + // Resolve the frame's width if ($this->_fixed_layout) { - list($frame_min, $frame_max) = array(0, 10e-10); + list($frame_min, $frame_max) = [0, 10e-10]; } else { list($frame_min, $frame_max) = $frame->get_min_max_width(); } @@ -648,12 +732,12 @@ class Cellmap $width = $style->width; $val = null; - if (Helpers::is_percent($width)) { + if (Helpers::is_percent($width) && $colspan === 1) { $var = "percent"; - $val = (float)rtrim($width, "% ") / $colspan; - } else if ($width !== "auto") { + $val = (float)rtrim($width, "% "); + } elseif ($width !== "auto" && $colspan === 1) { $var = "absolute"; - $val = $style->length_in_pt($frame_min) / $colspan; + $val = $frame_min; } $min = 0; @@ -661,7 +745,7 @@ class Cellmap for ($cs = 0; $cs < $colspan; $cs++) { // Resolve the frame's width(s) with other cells - $col =& $this->get_column($this->__col + $cs); + $col =& $this->get_column($first_col + $cs); // Note: $var is either 'percent' or 'absolute'. We compare the // requested percentage or absolute values with the existing widths @@ -675,12 +759,12 @@ class Cellmap $max += $col["max-width"]; } - if ($frame_min > $min) { + if ($frame_min > $min && $colspan === 1) { // The frame needs more space. Expand each sub-column // FIXME try to avoid putting this dummy value when table-layout:fixed - $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan); + $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min)); for ($c = 0; $c < $colspan; $c++) { - $col =& $this->get_column($this->__col + $c); + $col =& $this->get_column($first_col + $c); $col["min-width"] += $inc; } } @@ -689,22 +773,24 @@ class Cellmap // FIXME try to avoid putting this dummy value when table-layout:fixed $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan); for ($c = 0; $c < $colspan; $c++) { - $col =& $this->get_column($this->__col + $c); + $col =& $this->get_column($first_col + $c); $col["max-width"] += $inc; } } } - $this->__col += $colspan; - if ($this->__col > $this->_num_cols) { - $this->_num_cols = $this->__col; + // Adjust absolute columns so that the absolute (and max) width is the + // largest minimum width of all cells. This accounts for cells without + // absolute width within an absolute column + foreach ($this->_columns as &$col) { + if ($col["absolute"] > 0) { + $col["absolute"] = $col["min-width"]; + $col["max-width"] = $col["min-width"]; + } } } - /** - * - */ - public function add_row() + protected function add_row(): void { $this->__row++; $this->_num_rows++; @@ -727,7 +813,7 @@ class Cellmap { $key = $row->get_id(); if (!isset($this->_frames[$key])) { - return; // Presumably this row has alredy been removed + return; // Presumably this row has already been removed } $this->__row = $this->_num_rows--; @@ -775,7 +861,7 @@ class Cellmap { $key = $group->get_id(); if (!isset($this->_frames[$key])) { - return; // Presumably this row has alredy been removed + return; // Presumably this row has already been removed } $iter = $group->get_first_child(); @@ -797,16 +883,18 @@ class Cellmap public function update_row_group(Frame $group, Frame $last_row) { $g_key = $group->get_id(); - $r_key = $last_row->get_id(); - $r_rows = $this->_frames[$g_key]["rows"]; - $this->_frames[$g_key]["rows"] = range($this->_frames[$g_key]["rows"][0], end($r_rows)); + $first_index = $this->_frames[$g_key]["rows"][0]; + $last_index = $first_index; + $row = $last_row; + while ($row = $row->get_prev_sibling()) { + $last_index++; + } + + $this->_frames[$g_key]["rows"] = range($first_index, $last_index); } - /** - * - */ - public function assign_x_positions() + public function assign_x_positions(): void { // Pre-condition: widths must be resolved and assigned to columns and // column[0]["x"] must be set. @@ -822,17 +910,14 @@ class Cellmap } } - /** - * - */ - public function assign_frame_heights() + public function assign_frame_heights(): void { // Pre-condition: widths and heights of each column & row must be // calcluated foreach ($this->_frames as $arr) { $frame = $arr["frame"]; - $h = 0; + $h = 0.0; foreach ($arr["rows"] as $row) { if (!isset($this->_rows[$row])) { // The row has been removed because of a page split, so skip it. @@ -845,7 +930,7 @@ class Cellmap if ($frame instanceof TableCellFrameDecorator) { $frame->set_cell_height($h); } else { - $frame->get_style()->height = $h; + $frame->get_style()->set_used("height", $h); } } } @@ -853,13 +938,13 @@ class Cellmap /** * Re-adjust frame height if the table height is larger than its content */ - public function set_frame_heights($table_height, $content_height) + public function set_frame_heights(float $table_height, float $content_height): void { // Distribute the increased height proportionally amongst each row foreach ($this->_frames as $arr) { $frame = $arr["frame"]; - $h = 0; + $h = 0.0; foreach ($arr["rows"] as $row) { if (!isset($this->_rows[$row])) { continue; @@ -871,13 +956,13 @@ class Cellmap if ($content_height > 0) { $new_height = ($h / $content_height) * $table_height; } else { - $new_height = 0; + $new_height = 0.0; } if ($frame instanceof TableCellFrameDecorator) { $frame->set_cell_height($new_height); } else { - $frame->get_style()->height = $new_height; + $frame->get_style()->set_used("height", $new_height); } } } @@ -887,7 +972,7 @@ class Cellmap * * @return string */ - public function __toString() + public function __toString(): string { $str = ""; $str .= "Columns:
"; @@ -896,19 +981,19 @@ class Cellmap $str .= Helpers::pre_r($this->_rows, true); $str .= "Frames:
"; - $arr = array(); + $arr = []; foreach ($this->_frames as $key => $val) { - $arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]); + $arr[$key] = ["columns" => $val["columns"], "rows" => $val["rows"]]; } $str .= Helpers::pre_r($arr, true); if (php_sapi_name() == "cli") { - $str = strip_tags(str_replace(array("
", "", ""), - array("\n", chr(27) . "[01;33m", chr(27) . "[0m"), + $str = strip_tags(str_replace(["
", "", ""], + ["\n", chr(27) . "[01;33m", chr(27) . "[0m"], $str)); } return $str; } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/src/Css/AttributeTranslator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/AttributeTranslator.php similarity index 74% rename from library/vendor/dompdf/src/Css/AttributeTranslator.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/AttributeTranslator.php index c45d7413a..b2013e13c 100644 --- a/library/vendor/dompdf/src/Css/AttributeTranslator.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/AttributeTranslator.php @@ -1,14 +1,13 @@ - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Css; use Dompdf\Frame; +use Dompdf\Helpers; /** * Translates HTML 4.0 attributes into CSS rules @@ -22,33 +21,33 @@ class AttributeTranslator // Munged data originally from // http://www.w3.org/TR/REC-html40/index/attributes.html // http://www.cs.tut.fi/~jkorpela/html2css.html - static private $__ATTRIBUTE_LOOKUP = array( + private static $__ATTRIBUTE_LOOKUP = [ //'caption' => array ( 'align' => '', ), - 'img' => array( - 'align' => array( + 'img' => [ + 'align' => [ 'bottom' => 'vertical-align: baseline;', 'middle' => 'vertical-align: middle;', 'top' => 'vertical-align: top;', 'left' => 'float: left;', 'right' => 'float: right;' - ), + ], 'border' => 'border: %0.2Fpx solid;', - 'height' => 'height: %spx;', + 'height' => '_set_px_height', 'hspace' => 'padding-left: %1$0.2Fpx; padding-right: %1$0.2Fpx;', 'vspace' => 'padding-top: %1$0.2Fpx; padding-bottom: %1$0.2Fpx;', - 'width' => 'width: %spx;', - ), - 'table' => array( - 'align' => array( + 'width' => '_set_px_width', + ], + 'table' => [ + 'align' => [ 'left' => 'margin-left: 0; margin-right: auto;', 'center' => 'margin-left: auto; margin-right: auto;', 'right' => 'margin-left: auto; margin-right: 0;' - ), + ], 'bgcolor' => 'background-color: %s;', - 'border' => '!set_table_border', - 'cellpadding' => '!set_table_cellpadding', //'border-spacing: %0.2F; border-collapse: separate;', - 'cellspacing' => '!set_table_cellspacing', - 'frame' => array( + 'border' => '_set_table_border', + 'cellpadding' => '_set_table_cellpadding', //'border-spacing: %0.2F; border-collapse: separate;', + 'cellspacing' => '_set_table_cellspacing', + 'frame' => [ 'void' => 'border-style: none;', 'above' => 'border-top-style: solid;', 'below' => 'border-bottom-style: solid;', @@ -58,44 +57,44 @@ class AttributeTranslator 'rhs' => 'border-right-style: solid;', 'box' => 'border-style: solid;', 'border' => 'border-style: solid;' - ), - 'rules' => '!set_table_rules', + ], + 'rules' => '_set_table_rules', 'width' => 'width: %s;', - ), - 'hr' => array( - 'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly + ], + 'hr' => [ + 'align' => '_set_hr_align', // Need to grab width to set 'left' & 'right' correctly 'noshade' => 'border-style: solid;', - 'size' => '!set_hr_size', //'border-width: %0.2F px;', + 'size' => '_set_hr_size', //'border-width: %0.2F px;', 'width' => 'width: %s;', - ), - 'div' => array( + ], + 'div' => [ 'align' => 'text-align: %s;', - ), - 'h1' => array( + ], + 'h1' => [ 'align' => 'text-align: %s;', - ), - 'h2' => array( + ], + 'h2' => [ 'align' => 'text-align: %s;', - ), - 'h3' => array( + ], + 'h3' => [ 'align' => 'text-align: %s;', - ), - 'h4' => array( + ], + 'h4' => [ 'align' => 'text-align: %s;', - ), - 'h5' => array( + ], + 'h5' => [ 'align' => 'text-align: %s;', - ), - 'h6' => array( + ], + 'h6' => [ 'align' => 'text-align: %s;', - ), + ], //TODO: translate more form element attributes - 'input' => array( - 'size' => '!set_input_width' - ), - 'p' => array( + 'input' => [ + 'size' => '_set_input_width' + ], + 'p' => [ 'align' => 'text-align: %s;', - ), + ], // 'col' => array( // 'align' => '', // 'valign' => '', @@ -104,87 +103,87 @@ class AttributeTranslator // 'align' => '', // 'valign' => '', // ), - 'tbody' => array( - 'align' => '!set_table_row_align', - 'valign' => '!set_table_row_valign', - ), - 'td' => array( + 'tbody' => [ + 'align' => '_set_table_row_align', + 'valign' => '_set_table_row_valign', + ], + 'td' => [ 'align' => 'text-align: %s;', - 'bgcolor' => '!set_background_color', + 'bgcolor' => '_set_background_color', 'height' => 'height: %s;', 'nowrap' => 'white-space: nowrap;', 'valign' => 'vertical-align: %s;', 'width' => 'width: %s;', - ), - 'tfoot' => array( - 'align' => '!set_table_row_align', - 'valign' => '!set_table_row_valign', - ), - 'th' => array( + ], + 'tfoot' => [ + 'align' => '_set_table_row_align', + 'valign' => '_set_table_row_valign', + ], + 'th' => [ 'align' => 'text-align: %s;', - 'bgcolor' => '!set_background_color', + 'bgcolor' => '_set_background_color', 'height' => 'height: %s;', 'nowrap' => 'white-space: nowrap;', 'valign' => 'vertical-align: %s;', 'width' => 'width: %s;', - ), - 'thead' => array( - 'align' => '!set_table_row_align', - 'valign' => '!set_table_row_valign', - ), - 'tr' => array( - 'align' => '!set_table_row_align', - 'bgcolor' => '!set_table_row_bgcolor', - 'valign' => '!set_table_row_valign', - ), - 'body' => array( + ], + 'thead' => [ + 'align' => '_set_table_row_align', + 'valign' => '_set_table_row_valign', + ], + 'tr' => [ + 'align' => '_set_table_row_align', + 'bgcolor' => '_set_table_row_bgcolor', + 'valign' => '_set_table_row_valign', + ], + 'body' => [ 'background' => 'background-image: url(%s);', - 'bgcolor' => '!set_background_color', - 'link' => '!set_body_link', - 'text' => '!set_color', - ), - 'br' => array( + 'bgcolor' => '_set_background_color', + 'link' => '_set_body_link', + 'text' => '_set_color', + ], + 'br' => [ 'clear' => 'clear: %s;', - ), - 'basefont' => array( - 'color' => '!set_color', + ], + 'basefont' => [ + 'color' => '_set_color', 'face' => 'font-family: %s;', - 'size' => '!set_basefont_size', - ), - 'font' => array( - 'color' => '!set_color', + 'size' => '_set_basefont_size', + ], + 'font' => [ + 'color' => '_set_color', 'face' => 'font-family: %s;', - 'size' => '!set_font_size', - ), - 'dir' => array( + 'size' => '_set_font_size', + ], + 'dir' => [ 'compact' => 'margin: 0.5em 0;', - ), - 'dl' => array( + ], + 'dl' => [ 'compact' => 'margin: 0.5em 0;', - ), - 'menu' => array( + ], + 'menu' => [ 'compact' => 'margin: 0.5em 0;', - ), - 'ol' => array( + ], + 'ol' => [ 'compact' => 'margin: 0.5em 0;', 'start' => 'counter-reset: -dompdf-default-counter %d;', 'type' => 'list-style-type: %s;', - ), - 'ul' => array( + ], + 'ul' => [ 'compact' => 'margin: 0.5em 0;', 'type' => 'list-style-type: %s;', - ), - 'li' => array( + ], + 'li' => [ 'type' => 'list-style-type: %s;', 'value' => 'counter-reset: -dompdf-default-counter %d;', - ), - 'pre' => array( + ], + 'pre' => [ 'width' => 'width: %s;', - ), - ); + ], + ]; - static protected $_last_basefont_size = 3; - static protected $_font_size_lookup = array( + protected static $_last_basefont_size = 3; + protected static $_font_size_lookup = [ // For basefont support -3 => "4pt", -2 => "5pt", @@ -204,7 +203,7 @@ class AttributeTranslator 9 => "44pt", 10 => "52pt", 11 => "60pt", - ); + ]; /** * @param Frame $frame @@ -249,7 +248,6 @@ class AttributeTranslator $style = ltrim($style); $node->setAttribute(self::$_style_attr, $style); } - } /** @@ -259,13 +257,10 @@ class AttributeTranslator * * @return string */ - static protected function _resolve_target(\DOMNode $node, $target, $value) + protected static function _resolve_target(\DOMNode $node, $target, $value) { - if ($target[0] === "!") { - // Function call - $func = "_" . mb_substr($target, 1); - - return self::$func($node, $value); + if ($target[0] === "_") { + return self::$target($node, $value); } return $value ? sprintf($target, $value) : ""; @@ -288,7 +283,7 @@ class AttributeTranslator * * @return \DOMNodeList|\DOMElement[] */ - static protected function get_cell_list(\DOMNode $node) + protected static function get_cell_list(\DOMNode $node) { $xpath = new \DOMXpath($node->ownerDocument); @@ -317,7 +312,7 @@ class AttributeTranslator * * @return string */ - static protected function _get_valid_color($value) + protected static function _get_valid_color($value) { if (preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches)) { $value = "#$matches[1]"; @@ -332,7 +327,7 @@ class AttributeTranslator * * @return string */ - static protected function _set_color(\DOMElement $node, $value) + protected static function _set_color(\DOMElement $node, $value) { $value = self::_get_valid_color($value); @@ -345,20 +340,50 @@ class AttributeTranslator * * @return string */ - static protected function _set_background_color(\DOMElement $node, $value) + protected static function _set_background_color(\DOMElement $node, $value) { $value = self::_get_valid_color($value); return "background-color: $value;"; } + protected static function _set_px_width(\DOMElement $node, string $value): string + { + $v = trim($value); + + if (Helpers::is_percent($v)) { + return sprintf("width: %s;", $v); + } + + if (is_numeric(mb_substr($v, 0, 1))) { + return sprintf("width: %spx;", (float) $v); + } + + return ""; + } + + protected static function _set_px_height(\DOMElement $node, string $value): string + { + $v = trim($value); + + if (Helpers::is_percent($v)) { + return sprintf("height: %s;", $v); + } + + if (is_numeric(mb_substr($v, 0, 1))) { + return sprintf("height: %spx;", (float) $v); + } + + return ""; + } + /** * @param \DOMElement $node * @param string $value * * @return null */ - static protected function _set_table_cellpadding(\DOMElement $node, $value) + protected static function _set_table_cellpadding(\DOMElement $node, $value) { $cell_list = self::get_cell_list($node); @@ -375,21 +400,9 @@ class AttributeTranslator * * @return string */ - static protected function _set_table_border(\DOMElement $node, $value) + protected static function _set_table_border(\DOMElement $node, $value) { - $cell_list = self::get_cell_list($node); - - foreach ($cell_list as $cell) { - $style = rtrim($cell->getAttribute(self::$_style_attr)); - $style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;"; - $style = ltrim($style, ";"); - $cell->setAttribute(self::$_style_attr, $style); - } - - $style = rtrim($node->getAttribute(self::$_style_attr), ";"); - $style .= "; border-width: $value" . "px; "; - - return ltrim($style, "; "); + return "border-width: $value" . "px;"; } /** @@ -398,7 +411,7 @@ class AttributeTranslator * * @return string */ - static protected function _set_table_cellspacing(\DOMElement $node, $value) + protected static function _set_table_cellspacing(\DOMElement $node, $value) { $style = rtrim($node->getAttribute(self::$_style_attr), ";"); @@ -417,7 +430,7 @@ class AttributeTranslator * * @return null|string */ - static protected function _set_table_rules(\DOMElement $node, $value) + protected static function _set_table_rules(\DOMElement $node, $value) { $new_style = "; border-collapse: collapse;"; @@ -467,7 +480,7 @@ class AttributeTranslator * * @return string */ - static protected function _set_hr_size(\DOMElement $node, $value) + protected static function _set_hr_size(\DOMElement $node, $value) { $style = rtrim($node->getAttribute(self::$_style_attr), ";"); $style .= "; border-width: " . max(0, $value - 2) . "; "; @@ -481,7 +494,7 @@ class AttributeTranslator * * @return null|string */ - static protected function _set_hr_align(\DOMElement $node, $value) + protected static function _set_hr_align(\DOMElement $node, $value) { $style = rtrim($node->getAttribute(self::$_style_attr), ";"); $width = $node->getAttribute("width"); @@ -518,11 +531,11 @@ class AttributeTranslator * * @return null|string */ - static protected function _set_input_width(\DOMElement $node, $value) + protected static function _set_input_width(\DOMElement $node, $value) { if (empty($value)) { return null; } - if ($node->hasAttribute("type") && in_array(strtolower($node->getAttribute("type")), array("text","password"))) { + if ($node->hasAttribute("type") && in_array(strtolower($node->getAttribute("type")), ["text","password"])) { return sprintf("width: %Fem", (((int)$value * .65)+2)); } else { return sprintf("width: %upx;", (int)$value); @@ -535,7 +548,7 @@ class AttributeTranslator * * @return null */ - static protected function _set_table_row_align(\DOMElement $node, $value) + protected static function _set_table_row_align(\DOMElement $node, $value) { $cell_list = self::get_cell_list($node); @@ -552,7 +565,7 @@ class AttributeTranslator * * @return null */ - static protected function _set_table_row_valign(\DOMElement $node, $value) + protected static function _set_table_row_valign(\DOMElement $node, $value) { $cell_list = self::get_cell_list($node); @@ -569,7 +582,7 @@ class AttributeTranslator * * @return null */ - static protected function _set_table_row_bgcolor(\DOMElement $node, $value) + protected static function _set_table_row_bgcolor(\DOMElement $node, $value) { $cell_list = self::get_cell_list($node); $value = self::_get_valid_color($value); @@ -587,7 +600,7 @@ class AttributeTranslator * * @return null */ - static protected function _set_body_link(\DOMElement $node, $value) + protected static function _set_body_link(\DOMElement $node, $value) { $a_list = $node->getElementsByTagName("a"); $value = self::_get_valid_color($value); @@ -605,7 +618,7 @@ class AttributeTranslator * * @return null */ - static protected function _set_basefont_size(\DOMElement $node, $value) + protected static function _set_basefont_size(\DOMElement $node, $value) { // FIXME: ? we don't actually set the font size of anything here, just // the base size for later modification by tags. @@ -620,7 +633,7 @@ class AttributeTranslator * * @return string */ - static protected function _set_font_size(\DOMElement $node, $value) + protected static function _set_font_size(\DOMElement $node, $value) { $style = $node->getAttribute(self::$_style_attr); diff --git a/library/vendor/dompdf/src/Css/Color.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Color.php similarity index 69% rename from library/vendor/dompdf/src/Css/Color.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Color.php index 737666300..28a9f562c 100644 --- a/library/vendor/dompdf/src/Css/Color.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Color.php @@ -2,19 +2,16 @@ /** * @package dompdf - * @link http://dompdf.github.com/ - * @author Benj Carson - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\Css; use Dompdf\Helpers; class Color { - static $cssColorNames = array( + static $cssColorNames = [ "aliceblue" => "F0F8FF", "antiquewhite" => "FAEBD7", "aqua" => "00FFFF", @@ -162,21 +159,25 @@ class Color "whitesmoke" => "F5F5F5", "yellow" => "FFFF00", "yellowgreen" => "9ACD32", - ); + ]; /** - * @param $color - * @return array|mixed|null|string + * @param array|string|null $color + * @return array|string|null */ static function parse($color) { + if ($color === null) { + return null; + } + if (is_array($color)) { // Assume the array has the right format... // FIXME: should/could verify this. return $color; } - static $cache = array(); + static $cache = []; $color = strtolower($color); @@ -184,7 +185,7 @@ class Color return $cache[$color]; } - if (in_array($color, array("transparent", "inherit"))) { + if ($color === "transparent") { return $cache[$color] = $color; } @@ -192,24 +193,43 @@ class Color return $cache[$color] = self::getArray(self::$cssColorNames[$color]); } - $length = mb_strlen($color); + // https://www.w3.org/TR/css-color-4/#hex-notation + if (mb_substr($color, 0, 1) === "#") { + $length = mb_strlen($color); + $alpha = 1.0; - // #rgb format - if ($length == 4 && $color[0] === "#") { - return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3]); - } // #rgba format - else if ($length == 5 && $color[0] === "#") { - $alpha = round(hexdec($color[4] . $color[4])/255, 2); - return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3], $alpha); - } // #rrggbb format - else if ($length == 7 && $color[0] === "#") { - return $cache[$color] = self::getArray(mb_substr($color, 1, 6)); - } // #rrggbbaa format - else if ($length == 9 && $color[0] === "#") { - $alpha = round(hexdec(mb_substr($color, 7, 2))/255, 2); - return $cache[$color] = self::getArray(mb_substr($color, 1, 8), $alpha); - } // rgb( r,g,b ) / rgba( r,g,b,α ) format - else if (mb_strpos($color, "rgb") !== false) { + // #rgb format + if ($length === 4) { + return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3]); + } + + // #rgba format + if ($length === 5) { + if (ctype_xdigit($color[4])) { + $alpha = round(hexdec($color[4] . $color[4])/255, 2); + } + return $cache[$color] = self::getArray($color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3], $alpha); + } + + // #rrggbb format + if ($length === 7) { + return $cache[$color] = self::getArray(mb_substr($color, 1, 6)); + } + + // #rrggbbaa format + if ($length === 9) { + if (ctype_xdigit(mb_substr($color, 7, 2))) { + $alpha = round(hexdec(mb_substr($color, 7, 2))/255, 2); + } + return $cache[$color] = self::getArray(mb_substr($color, 1, 6), $alpha); + } + + return null; + } + + // rgb( r g b [/α] ) / rgb( r,g,b[,α] ) format and alias rgba() + // https://www.w3.org/TR/css-color-4/#rgb-functions + if (mb_substr($color, 0, 4) === "rgb(" || mb_substr($color, 0, 5) === "rgba(") { $i = mb_strpos($color, "("); $j = mb_strpos($color, ")"); @@ -218,38 +238,45 @@ class Color return null; } - $triplet = explode(",", mb_substr($color, $i + 1, $j - $i - 1)); + $value_decl = trim(mb_substr($color, $i + 1, $j - $i - 1)); - // alpha transparency - // FIXME: not currently using transparency - $alpha = 1.0; - if (count($triplet) == 4) { - $alpha = (float)(trim(array_pop($triplet))); - // bad value, set to fully opaque - if ($alpha > 1.0 || $alpha < 0.0) { - $alpha = 1.0; - } + if (mb_strpos($value_decl, ",") === false) { + // Space-separated values syntax `r g b` or `r g b / α` + $parts = preg_split("/\s*\/\s*/", $value_decl); + $triplet = preg_split("/\s+/", $parts[0]); + $alpha = $parts[1] ?? 1.0; + } else { + // Comma-separated values syntax `r, g, b` or `r, g, b, α` + $parts = preg_split("/\s*,\s*/", $value_decl); + $triplet = array_slice($parts, 0, 3); + $alpha = $parts[3] ?? 1.0; } - if (count($triplet) != 3) { + if (count($triplet) !== 3) { return null; } - foreach (array_keys($triplet) as $c) { - $triplet[$c] = trim($triplet[$c]); + // Parse alpha value + if (Helpers::is_percent($alpha)) { + $alpha = (float) $alpha / 100; + } else { + $alpha = (float) $alpha; + } - if (Helpers::is_percent($triplet[$c])) { - $triplet[$c] = round((float)$triplet[$c] * 2.55); + $alpha = max(0.0, min($alpha, 1.0)); + + foreach ($triplet as &$c) { + if (Helpers::is_percent($c)) { + $c = round((float) $c * 2.55); } } return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet), $alpha); - } // cmyk( c,m,y,k ) format // http://www.w3.org/TR/css3-gcpm/#cmyk-colors - else if (mb_strpos($color, "cmyk") !== false) { + if (mb_substr($color, 0, 5) === "cmyk(") { $i = mb_strpos($color, "("); $j = mb_strpos($color, ")"); @@ -264,24 +291,25 @@ class Color return null; } - $values = array_map(function($c) { + $values = array_map(function ($c) { return min(1.0, max(0.0, floatval(trim($c)))); }, $values); return $cache[$color] = self::getArray($values); } + // Invalid or unsupported color format return null; } /** - * @param $color + * @param array|string $color * @param float $alpha * @return array */ static function getArray($color, $alpha = 1.0) { - $c = array(null, null, null, null, "alpha" => $alpha, "hex" => null); + $c = [null, null, null, null, "alpha" => $alpha, "hex" => null]; if (is_array($color)) { $c = $color; @@ -292,6 +320,10 @@ class Color $c["alpha"] = $alpha; $c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])"; } else { + if (ctype_xdigit($color) === false || mb_strlen($color) !== 6) { + // invalid color value ... expected 6-character hex + return $c; + } $c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff; $c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff; $c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff; @@ -299,7 +331,7 @@ class Color $c["g"] = $c[1]; $c["b"] = $c[2]; $c["alpha"] = $alpha; - $c["hex"] = sprintf("#%s%02X", mb_substr($color, 0, 6), round($alpha * 255)); + $c["hex"] = sprintf("#%s%02X", $color, round($alpha * 255)); } return $c; diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Style.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Style.php new file mode 100644 index 000000000..2d4c9d798 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Style.php @@ -0,0 +1,3743 @@ +margin_top = 10.0; + * echo $style->margin_top; // Returns `10.0` + * ``` + * + * To declare a property from a string, use {@link Style::set_prop()}: + * + * ``` + * $style->set_prop("margin_top", "1em"); + * echo $style->get_specified("margin_top"); // Returns `1em` + * echo $style->margin_top; // Returns `12.0`, assuming the default font size + * ``` + * + * Actual CSS parsing is performed in the {@link Stylesheet} class. + * + * @property string $azimuth + * @property string $background_attachment + * @property array|string $background_color + * @property string $background_image Image URL or `none` + * @property string $background_image_resolution + * @property array $background_position + * @property string $background_repeat + * @property array|string $background_size `cover`, `contain`, or `[width, height]`, each being a length, percentage, or `auto` + * @property string $border_collapse + * @property string $border_color Only use for setting all sides to the same color + * @property float[] $border_spacing Pair of `[horizontal, vertical]` spacing + * @property string $border_style Only use for setting all sides to the same style + * @property array|string $border_top_color + * @property array|string $border_right_color + * @property array|string $border_bottom_color + * @property array|string $border_left_color + * @property string $border_top_style Valid border style + * @property string $border_right_style Valid border style + * @property string $border_bottom_style Valid border style + * @property string $border_left_style Valid border style + * @property float $border_top_width Length in pt + * @property float $border_right_width Length in pt + * @property float $border_bottom_width Length in pt + * @property float $border_left_width Length in pt + * @property string $border_width Only use for setting all sides to the same width + * @property float|string $border_bottom_left_radius Radius in pt or a percentage value + * @property float|string $border_bottom_right_radius Radius in pt or a percentage value + * @property float|string $border_top_left_radius Radius in pt or a percentage value + * @property float|string $border_top_right_radius Radius in pt or a percentage value + * @property string $border_radius Only use for setting all corners to the same radius + * @property float|string $bottom Length in pt, a percentage value, or `auto` + * @property string $caption_side + * @property string $clear + * @property string $clip + * @property array|string $color + * @property string[]|string $content List of content components, `normal`, or `none` + * @property array|string $counter_increment Array defining the counters to increment or `none` + * @property array|string $counter_reset Array defining the counters to reset or `none` + * @property string $cue_after + * @property string $cue_before + * @property string $cue + * @property string $cursor + * @property string $direction + * @property string $display + * @property string $elevation + * @property string $empty_cells + * @property string $float + * @property string $font_family + * @property float $font_size Length in pt + * @property string $font_style + * @property string $font_variant + * @property string $font_weight + * @property float|string $height Length in pt, a percentage value, or `auto` + * @property string $image_resolution + * @property string $inset Only use for setting all box insets to the same length + * @property float|string $left Length in pt, a percentage value, or `auto` + * @property float $letter_spacing Length in pt + * @property float $line_height Length in pt + * @property string $list_style_image Image URL or `none` + * @property string $list_style_position + * @property string $list_style_type + * @property float|string $margin_right Length in pt, a percentage value, or `auto` + * @property float|string $margin_left Length in pt, a percentage value, or `auto` + * @property float|string $margin_top Length in pt, a percentage value, or `auto` + * @property float|string $margin_bottom Length in pt, a percentage value, or `auto` + * @property string $margin Only use for setting all sides to the same length + * @property float|string $max_height Length in pt, a percentage value, or `none` + * @property float|string $max_width Length in pt, a percentage value, or `none` + * @property float|string $min_height Length in pt, a percentage value, or `auto` + * @property float|string $min_width Length in pt, a percentage value, or `auto` + * @property float $opacity Number in the range [0, 1] + * @property int $orphans + * @property array|string $outline_color + * @property string $outline_style Valid border style, except for `hidden` + * @property float $outline_width Length in pt + * @property float $outline_offset Length in pt + * @property string $overflow + * @property string $overflow_wrap + * @property float|string $padding_top Length in pt or a percentage value + * @property float|string $padding_right Length in pt or a percentage value + * @property float|string $padding_bottom Length in pt or a percentage value + * @property float|string $padding_left Length in pt or a percentage value + * @property string $padding Only use for setting all sides to the same length + * @property string $page_break_after + * @property string $page_break_before + * @property string $page_break_inside + * @property string $pause_after + * @property string $pause_before + * @property string $pause + * @property string $pitch_range + * @property string $pitch + * @property string $play_during + * @property string $position + * @property string $quotes + * @property string $richness + * @property float|string $right Length in pt, a percentage value, or `auto` + * @property float[]|string $size Pair of `[width, height]` or `auto` + * @property string $speak_header + * @property string $speak_numeral + * @property string $speak_punctuation + * @property string $speak + * @property string $speech_rate + * @property string $src + * @property string $stress + * @property string $table_layout + * @property string $text_align + * @property string $text_decoration + * @property float|string $text_indent Length in pt or a percentage value + * @property string $text_transform + * @property float|string $top Length in pt, a percentage value, or `auto` + * @property array $transform List of transforms + * @property array $transform_origin + * @property string $unicode_bidi + * @property string $unicode_range + * @property string $vertical_align + * @property string $visibility + * @property string $voice_family + * @property string $volume + * @property string $white_space + * @property int $widows + * @property float|string $width Length in pt, a percentage value, or `auto` + * @property string $word_break + * @property float $word_spacing Length in pt + * @property int|string $z_index Integer value or `auto` + * @property string $_dompdf_keep + * + * @package dompdf + */ +class Style +{ + protected const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*"; + protected const CSS_INTEGER = "[+-]?\d+"; + protected const CSS_NUMBER = "[+-]?\d*\.?\d+(?:[eE][+-]?\d+)?"; + + /** + * Default font size, in points. + * + * @var float + */ + public static $default_font_size = 12; + + /** + * Default line height, as a fraction of the font size. + * + * @var float + */ + public static $default_line_height = 1.2; + + /** + * Default "absolute" font sizes relative to the default font-size + * https://www.w3.org/TR/css-fonts-3/#absolute-size-value + * + * @var array + */ + public static $font_size_keywords = [ + "xx-small" => 0.6, // 3/5 + "x-small" => 0.75, // 3/4 + "small" => 0.889, // 8/9 + "medium" => 1, // 1 + "large" => 1.2, // 6/5 + "x-large" => 1.5, // 3/2 + "xx-large" => 2.0, // 2/1 + ]; + + /** + * List of valid text-align keywords. + */ + public const TEXT_ALIGN_KEYWORDS = ["left", "right", "center", "justify"]; + + /** + * List of valid vertical-align keywords. + */ + public const VERTICAL_ALIGN_KEYWORDS = ["baseline", "bottom", "middle", + "sub", "super", "text-bottom", "text-top", "top"]; + + /** + * List of all block-level (outer) display types. + * * https://www.w3.org/TR/css-display-3/#display-type + * * https://www.w3.org/TR/css-display-3/#block-level + */ + public const BLOCK_LEVEL_TYPES = [ + "block", + // "flow-root", + "list-item", + // "flex", + // "grid", + "table" + ]; + + /** + * List of all inline-level (outer) display types. + * * https://www.w3.org/TR/css-display-3/#display-type + * * https://www.w3.org/TR/css-display-3/#inline-level + */ + public const INLINE_LEVEL_TYPES = [ + "inline", + "inline-block", + // "inline-flex", + // "inline-grid", + "inline-table" + ]; + + /** + * List of all table-internal (outer) display types. + * * https://www.w3.org/TR/css-display-3/#layout-specific-display + */ + public const TABLE_INTERNAL_TYPES = [ + "table-row-group", + "table-header-group", + "table-footer-group", + "table-row", + "table-cell", + "table-column-group", + "table-column", + "table-caption" + ]; + + /** + * List of all inline (inner) display types. + */ + public const INLINE_TYPES = ["inline"]; + + /** + * List of all block (inner) display types. + */ + public const BLOCK_TYPES = ["block", "inline-block", "table-cell", "list-item"]; + + /** + * List of all table (inner) display types. + */ + public const TABLE_TYPES = ["table", "inline-table"]; + + /** + * Lookup table for valid display types. Initially computed from the + * different constants. + * + * @var array + */ + protected static $valid_display_types = []; + + /** + * List of all positioned types. + */ + public const POSITIONED_TYPES = ["relative", "absolute", "fixed"]; + + /** + * List of valid border styles. + */ + public const BORDER_STYLES = [ + "none", "hidden", + "dotted", "dashed", "solid", + "double", "groove", "ridge", "inset", "outset" + ]; + + /** + * List of valid outline-style values. + * Same as the border styles, except `auto` is allowed, `hidden` is not. + * + * @link https://www.w3.org/TR/css-ui-4/#typedef-outline-line-style + */ + protected const OUTLINE_STYLES = [ + "auto", "none", + "dotted", "dashed", "solid", + "double", "groove", "ridge", "inset", "outset" + ]; + + /** + * Map of CSS shorthand properties and their corresponding sub-properties. + * The order of the sub-properties is relevant for the fallback getter, + * which is used in case no specific getter method is defined. + * + * @var array + */ + protected static $_props_shorthand = [ + "background" => [ + "background_image", + "background_position", + "background_size", + "background_repeat", + // "background_origin", + // "background_clip", + "background_attachment", + "background_color" + ], + "border" => [ + "border_top_width", + "border_right_width", + "border_bottom_width", + "border_left_width", + "border_top_style", + "border_right_style", + "border_bottom_style", + "border_left_style", + "border_top_color", + "border_right_color", + "border_bottom_color", + "border_left_color" + ], + "border_top" => [ + "border_top_width", + "border_top_style", + "border_top_color" + ], + "border_right" => [ + "border_right_width", + "border_right_style", + "border_right_color" + ], + "border_bottom" => [ + "border_bottom_width", + "border_bottom_style", + "border_bottom_color" + ], + "border_left" => [ + "border_left_width", + "border_left_style", + "border_left_color" + ], + "border_width" => [ + "border_top_width", + "border_right_width", + "border_bottom_width", + "border_left_width" + ], + "border_style" => [ + "border_top_style", + "border_right_style", + "border_bottom_style", + "border_left_style" + ], + "border_color" => [ + "border_top_color", + "border_right_color", + "border_bottom_color", + "border_left_color" + ], + "border_radius" => [ + "border_top_left_radius", + "border_top_right_radius", + "border_bottom_right_radius", + "border_bottom_left_radius" + ], + "font" => [ + "font_family", + "font_size", + // "font_stretch", + "font_style", + "font_variant", + "font_weight", + "line_height" + ], + "inset" => [ + "top", + "right", + "bottom", + "left" + ], + "list_style" => [ + "list_style_image", + "list_style_position", + "list_style_type" + ], + "margin" => [ + "margin_top", + "margin_right", + "margin_bottom", + "margin_left" + ], + "padding" => [ + "padding_top", + "padding_right", + "padding_bottom", + "padding_left" + ], + "outline" => [ + "outline_width", + "outline_style", + "outline_color" + ] + ]; + + /** + * Maps legacy property names to actual property names. + * + * @var array + */ + protected static $_props_alias = [ + "word_wrap" => "overflow_wrap", + "_dompdf_background_image_resolution" => "background_image_resolution", + "_dompdf_image_resolution" => "image_resolution", + "_webkit_transform" => "transform", + "_webkit_transform_origin" => "transform_origin" + ]; + + /** + * Default style values. + * + * @link https://www.w3.org/TR/CSS21/propidx.html + * + * @var array + */ + protected static $_defaults = null; + + /** + * List of inherited properties + * + * @link https://www.w3.org/TR/CSS21/propidx.html + * + * @var array + */ + protected static $_inherited = null; + + /** + * Caches method_exists result + * + * @var array + */ + protected static $_methods_cache = []; + + /** + * The stylesheet this style belongs to + * + * @var Stylesheet + */ + protected $_stylesheet; + + /** + * Media queries attached to the style + * + * @var array + */ + protected $_media_queries; + + /** + * Properties set by an `!important` declaration. + * + * @var array + */ + protected $_important_props = []; + + /** + * Specified (or declared) values of the CSS properties. + * + * https://www.w3.org/TR/css-cascade-3/#value-stages + * + * @var array + */ + protected $_props = []; + + /** + * Computed values of the CSS properties. + * + * @var array + */ + protected $_props_computed = []; + + /** + * Used values of the CSS properties. + * + * @var array + */ + protected $_props_used = []; + + /** + * Marks properties with non-final used values that should be cleared on + * style reset. + * + * @var array + */ + protected $non_final_used = []; + + protected static $_dependency_map = [ + "border_top_style" => [ + "border_top_width" + ], + "border_bottom_style" => [ + "border_bottom_width" + ], + "border_left_style" => [ + "border_left_width" + ], + "border_right_style" => [ + "border_right_width" + ], + "direction" => [ + "text_align" + ], + "font_size" => [ + "background_position", + "background_size", + "border_top_width", + "border_right_width", + "border_bottom_width", + "border_left_width", + "border_top_left_radius", + "border_top_right_radius", + "border_bottom_right_radius", + "border_bottom_left_radius", + "letter_spacing", + "line_height", + "margin_top", + "margin_right", + "margin_bottom", + "margin_left", + "outline_width", + "outline_offset", + "padding_top", + "padding_right", + "padding_bottom", + "padding_left", + "word_spacing", + "width", + "height", + "min-width", + "min-height", + "max-width", + "max-height" + ], + "float" => [ + "display" + ], + "position" => [ + "display" + ], + "outline_style" => [ + "outline_width" + ] + ]; + + /** + * Lookup table for dependent properties. Initially computed from the + * dependency map. + * + * @var array + */ + protected static $_dependent_props = []; + + /** + * Style of the parent element in document tree. + * + * @var Style + */ + protected $parent_style; + + /** + * @var Frame|null + */ + protected $_frame; + + /** + * The origin of the style + * + * @var int + */ + protected $_origin = Stylesheet::ORIG_AUTHOR; + + /** + * The computed bottom spacing + * + * @var float|string|null + */ + private $_computed_bottom_spacing = null; + + /** + * @var bool|null + */ + private $has_border_radius_cache = null; + + /** + * @var array|null + */ + private $resolved_border_radius = null; + + /** + * @var FontMetrics + */ + private $fontMetrics; + + /** + * @param Stylesheet $stylesheet The stylesheet the style is associated with. + * @param int $origin + */ + public function __construct(Stylesheet $stylesheet, int $origin = Stylesheet::ORIG_AUTHOR) + { + $this->fontMetrics = $stylesheet->getFontMetrics(); + + $this->_stylesheet = $stylesheet; + $this->_media_queries = []; + $this->_origin = $origin; + $this->parent_style = null; + + if (!isset(self::$_defaults)) { + + // Shorthand + $d =& self::$_defaults; + + // All CSS 2.1 properties, and their default values + // Some properties are specified with their computed value for + // efficiency; this only works if the computed value is not + // dependent on another property + $d["azimuth"] = "center"; + $d["background_attachment"] = "scroll"; + $d["background_color"] = "transparent"; + $d["background_image"] = "none"; + $d["background_image_resolution"] = "normal"; + $d["background_position"] = ["0%", "0%"]; + $d["background_repeat"] = "repeat"; + $d["background"] = ""; + $d["border_collapse"] = "separate"; + $d["border_color"] = ""; + $d["border_spacing"] = [0.0, 0.0]; + $d["border_style"] = ""; + $d["border_top"] = ""; + $d["border_right"] = ""; + $d["border_bottom"] = ""; + $d["border_left"] = ""; + $d["border_top_color"] = "currentcolor"; + $d["border_right_color"] = "currentcolor"; + $d["border_bottom_color"] = "currentcolor"; + $d["border_left_color"] = "currentcolor"; + $d["border_top_style"] = "none"; + $d["border_right_style"] = "none"; + $d["border_bottom_style"] = "none"; + $d["border_left_style"] = "none"; + $d["border_top_width"] = "medium"; + $d["border_right_width"] = "medium"; + $d["border_bottom_width"] = "medium"; + $d["border_left_width"] = "medium"; + $d["border_width"] = ""; + $d["border_bottom_left_radius"] = 0.0; + $d["border_bottom_right_radius"] = 0.0; + $d["border_top_left_radius"] = 0.0; + $d["border_top_right_radius"] = 0.0; + $d["border_radius"] = ""; + $d["border"] = ""; + $d["bottom"] = "auto"; + $d["caption_side"] = "top"; + $d["clear"] = "none"; + $d["clip"] = "auto"; + $d["color"] = "#000000"; + $d["content"] = "normal"; + $d["counter_increment"] = "none"; + $d["counter_reset"] = "none"; + $d["cue_after"] = "none"; + $d["cue_before"] = "none"; + $d["cue"] = ""; + $d["cursor"] = "auto"; + $d["direction"] = "ltr"; + $d["display"] = "inline"; + $d["elevation"] = "level"; + $d["empty_cells"] = "show"; + $d["float"] = "none"; + $d["font_family"] = $stylesheet->get_dompdf()->getOptions()->getDefaultFont(); + $d["font_size"] = "medium"; + $d["font_style"] = "normal"; + $d["font_variant"] = "normal"; + $d["font_weight"] = "normal"; + $d["font"] = ""; + $d["height"] = "auto"; + $d["image_resolution"] = "normal"; + $d["inset"] = ""; + $d["left"] = "auto"; + $d["letter_spacing"] = "normal"; + $d["line_height"] = "normal"; + $d["list_style_image"] = "none"; + $d["list_style_position"] = "outside"; + $d["list_style_type"] = "disc"; + $d["list_style"] = ""; + $d["margin_right"] = 0.0; + $d["margin_left"] = 0.0; + $d["margin_top"] = 0.0; + $d["margin_bottom"] = 0.0; + $d["margin"] = ""; + $d["max_height"] = "none"; + $d["max_width"] = "none"; + $d["min_height"] = "auto"; + $d["min_width"] = "auto"; + $d["orphans"] = 2; + $d["outline_color"] = "currentcolor"; // "invert" special color is not supported + $d["outline_style"] = "none"; + $d["outline_width"] = "medium"; + $d["outline_offset"] = 0.0; + $d["outline"] = ""; + $d["overflow"] = "visible"; + $d["overflow_wrap"] = "normal"; + $d["padding_top"] = 0.0; + $d["padding_right"] = 0.0; + $d["padding_bottom"] = 0.0; + $d["padding_left"] = 0.0; + $d["padding"] = ""; + $d["page_break_after"] = "auto"; + $d["page_break_before"] = "auto"; + $d["page_break_inside"] = "auto"; + $d["pause_after"] = "0"; + $d["pause_before"] = "0"; + $d["pause"] = ""; + $d["pitch_range"] = "50"; + $d["pitch"] = "medium"; + $d["play_during"] = "auto"; + $d["position"] = "static"; + $d["quotes"] = "auto"; + $d["richness"] = "50"; + $d["right"] = "auto"; + $d["size"] = "auto"; // @page + $d["speak_header"] = "once"; + $d["speak_numeral"] = "continuous"; + $d["speak_punctuation"] = "none"; + $d["speak"] = "normal"; + $d["speech_rate"] = "medium"; + $d["stress"] = "50"; + $d["table_layout"] = "auto"; + $d["text_align"] = ""; + $d["text_decoration"] = "none"; + $d["text_indent"] = 0.0; + $d["text_transform"] = "none"; + $d["top"] = "auto"; + $d["unicode_bidi"] = "normal"; + $d["vertical_align"] = "baseline"; + $d["visibility"] = "visible"; + $d["voice_family"] = ""; + $d["volume"] = "medium"; + $d["white_space"] = "normal"; + $d["widows"] = 2; + $d["width"] = "auto"; + $d["word_break"] = "normal"; + $d["word_spacing"] = "normal"; + $d["z_index"] = "auto"; + + // CSS3 + $d["opacity"] = 1.0; + $d["background_size"] = ["auto", "auto"]; + $d["transform"] = "none"; + $d["transform_origin"] = "50% 50%"; + + // for @font-face + $d["src"] = ""; + $d["unicode_range"] = ""; + + // vendor-prefixed properties + $d["_dompdf_keep"] = ""; + + // Properties that inherit by default + self::$_inherited = [ + "azimuth", + "background_image_resolution", + "border_collapse", + "border_spacing", + "caption_side", + "color", + "cursor", + "direction", + "elevation", + "empty_cells", + "font_family", + "font_size", + "font_style", + "font_variant", + "font_weight", + "font", + "image_resolution", + "letter_spacing", + "line_height", + "list_style_image", + "list_style_position", + "list_style_type", + "list_style", + "orphans", + "overflow_wrap", + "pitch_range", + "pitch", + "quotes", + "richness", + "speak_header", + "speak_numeral", + "speak_punctuation", + "speak", + "speech_rate", + "stress", + "text_align", + "text_indent", + "text_transform", + "visibility", + "voice_family", + "volume", + "white_space", + "widows", + "word_break", + "word_spacing", + ]; + + // Compute dependent props from dependency map + foreach (self::$_dependency_map as $props) { + foreach ($props as $prop) { + self::$_dependent_props[$prop] = true; + } + } + + // Compute valid display-type lookup table + self::$valid_display_types = [ + "none" => true, + "-dompdf-br" => true, + "-dompdf-image" => true, + "-dompdf-list-bullet" => true, + "-dompdf-page" => true + ]; + foreach (self::BLOCK_LEVEL_TYPES as $val) { + self::$valid_display_types[$val] = true; + } + foreach (self::INLINE_LEVEL_TYPES as $val) { + self::$valid_display_types[$val] = true; + } + foreach (self::TABLE_INTERNAL_TYPES as $val) { + self::$valid_display_types[$val] = true; + } + } + } + + /** + * Clear all non-final used values. + * + * @return void + */ + public function reset(): void + { + foreach (array_keys($this->non_final_used) as $prop) { + unset($this->_props_used[$prop]); + } + + $this->non_final_used = []; + } + + /** + * @param array $media_queries + */ + public function set_media_queries(array $media_queries): void + { + $this->_media_queries = $media_queries; + } + + /** + * @return array + */ + public function get_media_queries(): array + { + return $this->_media_queries; + } + + /** + * @param Frame $frame + */ + public function set_frame(Frame $frame): void + { + $this->_frame = $frame; + } + + /** + * @return Frame|null + */ + public function get_frame(): ?Frame + { + return $this->_frame; + } + + /** + * @param int $origin + */ + public function set_origin(int $origin): void + { + $this->_origin = $origin; + } + + /** + * @return int + */ + public function get_origin(): int + { + return $this->_origin; + } + + /** + * Returns the {@link Stylesheet} the style is associated with. + * + * @return Stylesheet + */ + public function get_stylesheet(): Stylesheet + { + return $this->_stylesheet; + } + + public function is_absolute(): bool + { + $position = $this->__get("position"); + return $position === "absolute" || $position === "fixed"; + } + + public function is_in_flow(): bool + { + $float = $this->__get("float"); + return $float === "none" && !$this->is_absolute(); + } + + /** + * Converts any CSS length value into an absolute length in points. + * + * length_in_pt() takes a single length (e.g. '1em') or an array of + * lengths and returns an absolute length. If an array is passed, then + * the return value is the sum of all elements. If any of the lengths + * provided are "auto" or "none" then that value is returned. + * + * If a reference size is not provided, the current font size is used. + * + * @param float|string|array $length The numeric length (or string measurement) or array of lengths to resolve. + * @param float|null $ref_size An absolute reference size to resolve percentage lengths. + * + * @return float|string + */ + public function length_in_pt($length, ?float $ref_size = null) + { + $font_size = $this->__get("font_size"); + $ref_size = $ref_size ?? $font_size; + + if (!\is_array($length)) { + $length = [$length]; + } + + $ret = 0.0; + + foreach ($length as $l) { + if ($l === "auto" || $l === "none") { + return $l; + } + + // Assume numeric values are already in points + if (is_numeric($l)) { + $ret += (float) $l; + continue; + } + + $val = $this->single_length_in_pt((string) $l, $ref_size, $font_size); + $ret += $val ?? 0; + } + + return $ret; + } + + /** + * Convert a length declaration to pt. + * + * @param string $l The length declaration. + * @param float $ref_size Reference size for percentage declarations. + * @param float|null $font_size Font size for resolving font-size relative units. + * + * @return float|null The length in pt, or `null` for invalid declarations. + */ + protected function single_length_in_pt(string $l, float $ref_size = 0, ?float $font_size = null): ?float + { + static $cache = []; + + $font_size = $font_size ?? $this->__get("font_size"); + + $key = "$l/$ref_size/$font_size"; + + if (\array_key_exists($key, $cache)) { + return $cache[$key]; + } + + $number = self::CSS_NUMBER; + $pattern = "/^($number)(.*)?$/"; + + if (!preg_match($pattern, $l, $matches)) { + return null; + } + + $v = (float) $matches[1]; + $unit = mb_strtolower($matches[2]); + + if ($unit === "") { + // Legacy support for unitless values, not covered by spec. Might + // want to restrict this to unitless `0` in the future + $value = $v; + } + + elseif ($unit === "%") { + $value = $v / 100 * $ref_size; + } + + elseif ($unit === "px") { + $dpi = $this->_stylesheet->get_dompdf()->getOptions()->getDpi(); + $value = ($v * 72) / $dpi; + } + + elseif ($unit === "pt") { + $value = $v; + } + + elseif ($unit === "rem") { + $tree = $this->_stylesheet->get_dompdf()->getTree(); + $root_style = $tree !== null ? $tree->get_root()->get_style() : null; + $root_font_size = $root_style === null || $root_style === $this + ? $font_size + : $root_style->__get("font_size"); + $value = $v * $root_font_size; + + // Skip caching if the root style is not available yet, as to avoid + // incorrectly cached values if the root font size is different from + // the default + if ($root_style === null) { + return $value; + } + } + + elseif ($unit === "em") { + $value = $v * $font_size; + } + + elseif ($unit === "cm") { + $value = $v * 72 / 2.54; + } + + elseif ($unit === "mm") { + $value = $v * 72 / 25.4; + } + + elseif ($unit === "ex") { + // FIXME: em:ex ratio? + $value = $v * $font_size / 2; + } + + elseif ($unit === "in") { + $value = $v * 72; + } + + elseif ($unit === "pc") { + $value = $v * 12; + } + + else { + // Invalid or unsupported declaration + $value = null; + } + + return $cache[$key] = $value; + } + + /** + * Resolve inherited property values using the provided parent style or the + * default values, in case no parent style exists. + * + * https://www.w3.org/TR/css-cascade-3/#inheriting + * + * @param Style|null $parent + */ + public function inherit(?Style $parent = null): void + { + $this->parent_style = $parent; + + // Clear the computed font size, as it might depend on the parent + // font size + unset($this->_props_computed["font_size"]); + unset($this->_props_used["font_size"]); + + if ($parent) { + foreach (self::$_inherited as $prop) { + // For properties that inherit by default: When the cascade did + // not result in a value, inherit the parent value. Inheritance + // is handled via the specific sub-properties for shorthands + if (isset($this->_props[$prop]) || isset(self::$_props_shorthand[$prop])) { + continue; + } + + if (isset($parent->_props[$prop])) { + $parent_val = $parent->computed($prop); + + $this->_props[$prop] = $parent_val; + $this->_props_computed[$prop] = $parent_val; + $this->_props_used[$prop] = null; + } + } + } + + foreach ($this->_props as $prop => $val) { + if ($val === "inherit") { + if ($parent && isset($parent->_props[$prop])) { + $parent_val = $parent->computed($prop); + + $this->_props[$prop] = $parent_val; + $this->_props_computed[$prop] = $parent_val; + $this->_props_used[$prop] = null; + } else { + // Parent prop not set, use default + $this->_props[$prop] = self::$_defaults[$prop]; + unset($this->_props_computed[$prop]); + unset($this->_props_used[$prop]); + } + } + } + } + + /** + * Override properties in this style with those in $style + * + * @param Style $style + */ + public function merge(Style $style): void + { + foreach ($style->_props as $prop => $val) { + $important = isset($style->_important_props[$prop]); + + // `!important` declarations take precedence over normal ones + if (!$important && isset($this->_important_props[$prop])) { + continue; + } + + if ($important) { + $this->_important_props[$prop] = true; + } + + $this->_props[$prop] = $val; + + // Copy an existing computed value only for non-dependent + // properties; otherwise it may be invalid for the current style + if (!isset(self::$_dependent_props[$prop]) + && \array_key_exists($prop, $style->_props_computed) + ) { + $this->_props_computed[$prop] = $style->_props_computed[$prop]; + $this->_props_used[$prop] = null; + } else { + unset($this->_props_computed[$prop]); + unset($this->_props_used[$prop]); + } + } + } + + /** + * Clear information about important declarations after the style has been + * finalized during stylesheet loading. + */ + public function clear_important(): void + { + $this->_important_props = []; + } + + /** + * Clear border-radius and bottom-spacing cache as necessary when a given + * property is set. + * + * @param string $prop The property that is set. + */ + protected function clear_cache(string $prop): void + { + // Clear border-radius cache on setting any border-radius + // property + if ($prop === "border_top_left_radius" + || $prop === "border_top_right_radius" + || $prop === "border_bottom_left_radius" + || $prop === "border_bottom_right_radius" + ) { + $this->has_border_radius_cache = null; + $this->resolved_border_radius = null; + } + + // Clear bottom-spacing cache if necessary. Border style can + // disable/enable border calculations + if ($prop === "margin_bottom" + || $prop === "padding_bottom" + || $prop === "border_bottom_width" + || $prop === "border_bottom_style" + ) { + $this->_computed_bottom_spacing = null; + } + } + + /** + * Set a style property from a value declaration. + * + * Setting `$clear_dependencies` to `false` is useful for saving a bit of + * unnecessary work while loading stylesheets. + * + * @param string $prop The property to set. + * @param mixed $val The value declaration or computed value. + * @param bool $important Whether the declaration is important. + * @param bool $clear_dependencies Whether to clear computed values of dependent properties. + */ + public function set_prop(string $prop, $val, bool $important = false, bool $clear_dependencies = true): void + { + $prop = str_replace("-", "_", $prop); + + // Legacy property aliases + if (isset(self::$_props_alias[$prop])) { + $prop = self::$_props_alias[$prop]; + } + + if (!isset(self::$_defaults[$prop])) { + global $_dompdf_warnings; + $_dompdf_warnings[] = "'$prop' is not a recognized CSS property."; + return; + } + + if ($prop !== "content" && \is_string($val) && mb_strpos($val, "url") === false && mb_strlen($val) > 1) { + $val = mb_strtolower(trim(str_replace(["\n", "\t"], [" "], $val))); + } + + if (isset(self::$_props_shorthand[$prop])) { + // Shorthand properties directly set their respective sub-properties + // https://www.w3.org/TR/css-cascade-3/#shorthand + if ($val === "initial" || $val === "inherit" || $val === "unset") { + foreach (self::$_props_shorthand[$prop] as $sub_prop) { + $this->set_prop($sub_prop, $val, $important, $clear_dependencies); + } + } else { + $method = "_set_$prop"; + + if (!isset(self::$_methods_cache[$method])) { + self::$_methods_cache[$method] = method_exists($this, $method); + } + + if (self::$_methods_cache[$method]) { + $values = $this->$method($val); + + if ($values === []) { + return; + } + + // Each missing sub-property is assigned its initial value + // https://www.w3.org/TR/css-cascade-3/#shorthand + foreach (self::$_props_shorthand[$prop] as $sub_prop) { + $sub_val = $values[$sub_prop] ?? self::$_defaults[$sub_prop]; + $this->set_prop($sub_prop, $sub_val, $important, $clear_dependencies); + } + } + } + } else { + // Legacy support for `word-break: break-word` + // https://www.w3.org/TR/css-text-3/#valdef-word-break-break-word + if ($prop === "word_break" && $val === "break-word") { + $val = "normal"; + $this->set_prop("overflow_wrap", "anywhere", $important, $clear_dependencies); + } + + // `!important` declarations take precedence over normal ones + if (!$important && isset($this->_important_props[$prop])) { + return; + } + + if ($important) { + $this->_important_props[$prop] = true; + } + + // https://www.w3.org/TR/css-cascade-3/#inherit-initial + if ($val === "unset") { + $val = \in_array($prop, self::$_inherited, true) + ? "inherit" + : "initial"; + } + + // https://www.w3.org/TR/css-cascade-3/#valdef-all-initial + if ($val === "initial") { + $val = self::$_defaults[$prop]; + } + + $computed = $this->compute_prop($prop, $val); + + // Skip invalid declarations + if ($computed === null) { + return; + } + + $this->_props[$prop] = $val; + $this->_props_computed[$prop] = $computed; + $this->_props_used[$prop] = null; + + if ($clear_dependencies) { + // Clear the computed values of any dependent properties, so + // they can be re-computed + if (isset(self::$_dependency_map[$prop])) { + foreach (self::$_dependency_map[$prop] as $dependent) { + unset($this->_props_computed[$dependent]); + unset($this->_props_used[$dependent]); + } + } + + $this->clear_cache($prop); + } + } + } + + /** + * Get the specified value of a style property. + * + * @param string $prop + * + * @return mixed + * @throws Exception + */ + public function get_specified(string $prop) + { + // Legacy property aliases + if (isset(self::$_props_alias[$prop])) { + $prop = self::$_props_alias[$prop]; + } + + if (!isset(self::$_defaults[$prop])) { + throw new Exception("'$prop' is not a recognized CSS property."); + } + + return $this->_props[$prop] ?? self::$_defaults[$prop]; + } + + /** + * Set a style property to its final value. + * + * This sets the specified and used value of the style property to the given + * value, meaning the value is not parsed and thus should have a type + * compatible with the property. + * + * If a shorthand property is specified, all of its sub-properties are set + * to the given value. + * + * @param string $prop The property to set. + * @param mixed $val The final value of the property. + * + * @throws Exception + */ + public function __set(string $prop, $val) + { + // Legacy property aliases + if (isset(self::$_props_alias[$prop])) { + $prop = self::$_props_alias[$prop]; + } + + if (!isset(self::$_defaults[$prop])) { + throw new Exception("'$prop' is not a recognized CSS property."); + } + + if (isset(self::$_props_shorthand[$prop])) { + foreach (self::$_props_shorthand[$prop] as $sub_prop) { + $this->__set($sub_prop, $val); + } + } else { + $this->_props[$prop] = $val; + $this->_props_computed[$prop] = $val; + $this->_props_used[$prop] = $val; + + $this->clear_cache($prop); + } + } + + /** + * Set the used value of a style property. + * + * Used values are cleared on style reset. + * + * If a shorthand property is specified, all of its sub-properties are set + * to the given value. + * + * @param string $prop The property to set. + * @param mixed $val The used value of the property. + * + * @throws Exception + */ + public function set_used(string $prop, $val): void + { + // Legacy property aliases + if (isset(self::$_props_alias[$prop])) { + $prop = self::$_props_alias[$prop]; + } + + if (!isset(self::$_defaults[$prop])) { + throw new Exception("'$prop' is not a recognized CSS property."); + } + + if (isset(self::$_props_shorthand[$prop])) { + foreach (self::$_props_shorthand[$prop] as $sub_prop) { + $this->set_used($sub_prop, $val); + } + } else { + $this->_props_used[$prop] = $val; + $this->non_final_used[$prop] = true; + } + } + + /** + * Get the used or computed value of a style property, depending on whether + * the used value has been determined yet. + * + * @param string $prop + * + * @return mixed + * @throws Exception + */ + public function __get(string $prop) + { + // Legacy property aliases + if (isset(self::$_props_alias[$prop])) { + $prop = self::$_props_alias[$prop]; + } + + if (!isset(self::$_defaults[$prop])) { + throw new Exception("'$prop' is not a recognized CSS property."); + } + + if (isset($this->_props_used[$prop])) { + return $this->_props_used[$prop]; + } + + $method = "_get_$prop"; + + if (!isset(self::$_methods_cache[$method])) { + self::$_methods_cache[$method] = method_exists($this, $method); + } + + if (isset(self::$_props_shorthand[$prop])) { + // Don't cache shorthand values, always use getter. If no dedicated + // getter exists, use a simple fallback getter concatenating all + // sub-property values + if (self::$_methods_cache[$method]) { + return $this->$method(); + } else { + return implode(" ", array_map(function ($sub_prop) { + $val = $this->__get($sub_prop); + return \is_array($val) ? implode(" ", $val) : $val; + }, self::$_props_shorthand[$prop])); + } + } else { + $computed = $this->computed($prop); + $used = self::$_methods_cache[$method] + ? $this->$method($computed) + : $computed; + + $this->_props_used[$prop] = $used; + return $used; + } + } + + /** + * @param string $prop The property to compute. + * @param mixed $val The value to compute. Non-string values are treated as already computed. + * + * @return mixed The computed value. + */ + protected function compute_prop(string $prop, $val) + { + // During style merge, the parent style is not available yet, so + // temporarily use the initial value for `inherit` properties. The + // keyword is properly resolved during inheritance + if ($val === "inherit") { + $val = self::$_defaults[$prop]; + } + + // Check for values which are already computed + if (!\is_string($val)) { + return $val; + } + + $method = "_compute_$prop"; + + if (!isset(self::$_methods_cache[$method])) { + self::$_methods_cache[$method] = method_exists($this, $method); + } + + if (self::$_methods_cache[$method]) { + return $this->$method($val); + } elseif ($val !== "") { + return $val; + } else { + return null; + } + } + + /** + * Get the computed value for the given property. + * + * @param string $prop The property to get the computed value of. + * + * @return mixed The computed value. + */ + protected function computed(string $prop) + { + if (!\array_key_exists($prop, $this->_props_computed)) { + $val = $this->_props[$prop] ?? self::$_defaults[$prop]; + $computed = $this->compute_prop($prop, $val); + + $this->_props_computed[$prop] = $computed; + } + + return $this->_props_computed[$prop]; + } + + /** + * @param float $cbw The width of the containing block. + * @return float|string|null + */ + public function computed_bottom_spacing(float $cbw) + { + // Caching the bottom spacing independently of the given width is a bit + // iffy, but should be okay, as the containing block should only + // potentially change after a page break, and the style is reset in that + // case + if ($this->_computed_bottom_spacing !== null) { + return $this->_computed_bottom_spacing; + } + return $this->_computed_bottom_spacing = $this->length_in_pt( + [ + $this->margin_bottom, + $this->padding_bottom, + $this->border_bottom_width + ], + $cbw + ); + } + + /** + * Returns an `array(r, g, b, "r" => r, "g" => g, "b" => b, "alpha" => alpha, "hex" => "#rrggbb")` + * based on the provided CSS color value. + * + * @param string|null $color + * @return array|string|null + */ + public function munge_color($color) + { + return Color::parse($color); + } + + /** + * @return string + */ + public function get_font_family_raw(): string + { + return trim($this->_props["font_family"], " \t\n\r\x0B\"'"); + } + + /** + * Getter for the `font-family` CSS property. + * + * Uses the {@link FontMetrics} class to resolve the font family into an + * actual font file. + * + * @param string $computed + * @return string + * @throws Exception + * + * @link https://www.w3.org/TR/CSS21/fonts.html#propdef-font-family + */ + protected function _get_font_family($computed): string + { + //TODO: we should be using the calculated prop rather than perform the entire family parsing operation again + + $fontMetrics = $this->getFontMetrics(); + $DEBUGCSS = $this->_stylesheet->get_dompdf()->getOptions()->getDebugCss(); + + // Select the appropriate font. First determine the subtype, then check + // the specified font-families for a candidate. + + // Resolve font-weight + $weight = $this->__get("font_weight"); + if ($weight === 'bold') { + $weight = 700; + } elseif (preg_match('/^[0-9]+$/', $weight, $match)) { + $weight = (int)$match[0]; + } else { + $weight = 400; + } + + // Resolve font-style + $font_style = $this->__get("font_style"); + $subtype = $fontMetrics->getType($weight . ' ' . $font_style); + + $families = preg_split("/\s*,\s*/", $computed); + + $font = null; + foreach ($families as $family) { + //remove leading and trailing string delimiters, e.g. on font names with spaces; + //remove leading and trailing whitespace + $family = trim($family, " \t\n\r\x0B\"'"); + if ($DEBUGCSS) { + print '(' . $family . ')'; + } + $font = $fontMetrics->getFont($family, $subtype); + + if ($font) { + if ($DEBUGCSS) { + print "
[get_font_family:";
+                    print '(' . $computed . '.' . $font_style . '.' . $weight . '.' . $subtype . ')';
+                    print '(' . $font . ")get_font_family]\n
"; + } + return $font; + } + } + + $family = null; + if ($DEBUGCSS) { + print '(default)'; + } + $font = $fontMetrics->getFont($family, $subtype); + + if ($font) { + if ($DEBUGCSS) { + print '(' . $font . ")get_font_family]\n"; + } + return $font; + } + + throw new Exception("Unable to find a suitable font replacement for: '" . $computed . "'"); + } + + /** + * @param float|string $computed + * @return float + * + * @link https://www.w3.org/TR/css-text-4/#word-spacing-property + */ + protected function _get_word_spacing($computed) + { + if (\is_float($computed)) { + return $computed; + } + + // Resolve percentage values + $font_size = $this->__get("font_size"); + return $this->single_length_in_pt($computed, $font_size); + } + + /** + * @param float|string $computed + * @return float + * + * @link https://www.w3.org/TR/css-text-4/#letter-spacing-property + */ + protected function _get_letter_spacing($computed) + { + if (\is_float($computed)) { + return $computed; + } + + // Resolve percentage values + $font_size = $this->__get("font_size"); + return $this->single_length_in_pt($computed, $font_size); + } + + /** + * @param float|string $computed + * @return float + * + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-line-height + */ + protected function _get_line_height($computed) + { + // Lengths have been computed to float, number values to string + if (\is_float($computed)) { + return $computed; + } + + $font_size = $this->__get("font_size"); + $factor = $computed === "normal" + ? self::$default_line_height + : (float) $computed; + + return $factor * $font_size; + } + + /** + * @param string $computed + * @param bool $current_is_parent + * + * @return array|string + */ + protected function get_color_value($computed, bool $current_is_parent = false) + { + if ($computed === "currentcolor") { + // https://www.w3.org/TR/css-color-4/#resolving-other-colors + if ($current_is_parent) { + // Use the `color` value from the parent for the `color` + // property itself + return isset($this->parent_style) + ? $this->parent_style->__get("color") + : $this->munge_color(self::$_defaults["color"]); + } + + return $this->__get("color"); + } + + return $this->munge_color($computed) ?? "transparent"; + } + + /** + * Returns the color as an array + * + * The array has the following format: + * `array(r, g, b, "r" => r, "g" => g, "b" => b, "alpha" => alpha, "hex" => "#rrggbb")` + * + * @param string $computed + * @return array|string + * + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-color + */ + protected function _get_color($computed) + { + return $this->get_color_value($computed, true); + } + + /** + * Returns the background color as an array + * + * See {@link Style::_get_color()} for format of the color array. + * + * @param string $computed + * @return array|string + * + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-color + */ + protected function _get_background_color($computed) + { + return $this->get_color_value($computed); + } + + /** + * Returns the background image URI, or "none" + * + * @param string $computed + * @return string + * + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image + */ + protected function _get_background_image($computed): string + { + return $this->_stylesheet->resolve_url($computed); + } + + /** + * Returns the border color as an array + * + * See {@link Style::_get_color()} for format of the color array. + * + * @param string $computed + * @return array|string + * + * @link https://www.w3.org/TR/CSS21/box.html#border-color-properties + */ + protected function _get_border_top_color($computed) + { + return $this->get_color_value($computed); + } + + /** + * @param string $computed + * @return array|string + */ + protected function _get_border_right_color($computed) + { + return $this->get_color_value($computed); + } + + /** + * @param string $computed + * @return array|string + */ + protected function _get_border_bottom_color($computed) + { + return $this->get_color_value($computed); + } + + /** + * @param string $computed + * @return array|string + */ + protected function _get_border_left_color($computed) + { + return $this->get_color_value($computed); + } + + /** + * Return an array of all border properties. + * + * The returned array has the following structure: + * + * ``` + * array("top" => array("width" => [border-width], + * "style" => [border-style], + * "color" => [border-color (array)]), + * "bottom" ... ) + * ``` + * + * @return array + */ + public function get_border_properties(): array + { + return [ + "top" => [ + "width" => $this->__get("border_top_width"), + "style" => $this->__get("border_top_style"), + "color" => $this->__get("border_top_color"), + ], + "bottom" => [ + "width" => $this->__get("border_bottom_width"), + "style" => $this->__get("border_bottom_style"), + "color" => $this->__get("border_bottom_color"), + ], + "right" => [ + "width" => $this->__get("border_right_width"), + "style" => $this->__get("border_right_style"), + "color" => $this->__get("border_right_color"), + ], + "left" => [ + "width" => $this->__get("border_left_width"), + "style" => $this->__get("border_left_style"), + "color" => $this->__get("border_left_color"), + ], + ]; + } + + /** + * Return a single border-side property + * + * @param string $side + * @return string + */ + protected function get_border_side(string $side): string + { + $color = $this->__get("border_{$side}_color"); + + return $this->__get("border_{$side}_width") . " " . + $this->__get("border_{$side}_style") . " " . + (\is_array($color) ? $color["hex"] : $color); + } + + /** + * Return full border properties as a string + * + * Border properties are returned just as specified in CSS: + * `[width] [style] [color]` + * e.g. "1px solid blue" + * + * @return string + * + * @link https://www.w3.org/TR/CSS21/box.html#border-shorthand-properties + */ + protected function _get_border_top(): string + { + return $this->get_border_side("top"); + } + + /** + * @return string + */ + protected function _get_border_right(): string + { + return $this->get_border_side("right"); + } + + /** + * @return string + */ + protected function _get_border_bottom(): string + { + return $this->get_border_side("bottom"); + } + + /** + * @return string + */ + protected function _get_border_left(): string + { + return $this->get_border_side("left"); + } + + public function has_border_radius(): bool + { + if (isset($this->has_border_radius_cache)) { + return $this->has_border_radius_cache; + } + + // Use a fixed ref size here. We don't know the border-box width here + // and font size might be 0. Since we are only interested in whether + // there is any border radius at all, this should do + $tl = (float) $this->length_in_pt($this->border_top_left_radius, 12); + $tr = (float) $this->length_in_pt($this->border_top_right_radius, 12); + $br = (float) $this->length_in_pt($this->border_bottom_right_radius, 12); + $bl = (float) $this->length_in_pt($this->border_bottom_left_radius, 12); + + $this->has_border_radius_cache = $tl + $tr + $br + $bl > 0; + return $this->has_border_radius_cache; + } + + /** + * Get the final border-radius values to use. + * + * Percentage values are resolved relative to the width of the border box. + * The border radius is additionally scaled for the given render box, and + * constrained by its width and height. + * + * @param float[] $border_box The border box of the frame. + * @param float[]|null $render_box The box to resolve the border radius for. + * + * @return float[] A 4-tuple of top-left, top-right, bottom-right, and bottom-left radius. + */ + public function resolve_border_radius( + array $border_box, + ?array $render_box = null + ): array { + $render_box = $render_box ?? $border_box; + $use_cache = $render_box === $border_box; + + if ($use_cache && isset($this->resolved_border_radius)) { + return $this->resolved_border_radius; + } + + [$x, $y, $w, $h] = $border_box; + + // Resolve percentages relative to width, as long as we have no support + // for per-axis radii + $tl = (float) $this->length_in_pt($this->border_top_left_radius, $w); + $tr = (float) $this->length_in_pt($this->border_top_right_radius, $w); + $br = (float) $this->length_in_pt($this->border_bottom_right_radius, $w); + $bl = (float) $this->length_in_pt($this->border_bottom_left_radius, $w); + + if ($tl + $tr + $br + $bl > 0) { + [$rx, $ry, $rw, $rh] = $render_box; + + $t_offset = $y - $ry; + $r_offset = $rx + $rw - $x - $w; + $b_offset = $ry + $rh - $y - $h; + $l_offset = $x - $rx; + + if ($tl > 0) { + $tl = max($tl + ($t_offset + $l_offset) / 2, 0); + } + if ($tr > 0) { + $tr = max($tr + ($t_offset + $r_offset) / 2, 0); + } + if ($br > 0) { + $br = max($br + ($b_offset + $r_offset) / 2, 0); + } + if ($bl > 0) { + $bl = max($bl + ($b_offset + $l_offset) / 2, 0); + } + + if ($tl + $bl > $rh) { + $f = $rh / ($tl + $bl); + $tl = $f * $tl; + $bl = $f * $bl; + } + if ($tr + $br > $rh) { + $f = $rh / ($tr + $br); + $tr = $f * $tr; + $br = $f * $br; + } + if ($tl + $tr > $rw) { + $f = $rw / ($tl + $tr); + $tl = $f * $tl; + $tr = $f * $tr; + } + if ($bl + $br > $rw) { + $f = $rw / ($bl + $br); + $bl = $f * $bl; + $br = $f * $br; + } + } + + $values = [$tl, $tr, $br, $bl]; + + if ($use_cache) { + $this->resolved_border_radius = $values; + } + + return $values; + } + + /** + * Returns the outline color as an array + * + * See {@link Style::_get_color()} for format of the color array. + * + * @param string $computed + * @return array|string + * + * @link https://www.w3.org/TR/css-ui-4/#propdef-outline-color + */ + protected function _get_outline_color($computed) + { + return $this->get_color_value($computed); + } + + /** + * @param string $computed + * @return string + * + * @link https://www.w3.org/TR/css-ui-4/#propdef-outline-style + */ + protected function _get_outline_style($computed): string + { + return $computed === "auto" ? "solid" : $computed; + } + + /** + * Return full outline properties as a string + * + * Outline properties are returned just as specified in CSS: + * `[width] [style] [color]` + * e.g. "1px solid blue" + * + * @return string + * + * @link https://www.w3.org/TR/CSS21/box.html#border-shorthand-properties + */ + protected function _get_outline(): string + { + $color = $this->__get("outline_color"); + + return $this->__get("outline_width") . " " . + $this->__get("outline_style") . " " . + (\is_array($color) ? $color["hex"] : $color); + } + + /** + * Returns the list style image URI, or "none" + * + * @param string $computed + * @return string + * + * @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image + */ + protected function _get_list_style_image($computed): string + { + return $this->_stylesheet->resolve_url($computed); + } + + /** + * @param string $value + * @param int $default + * + * @return array|string + */ + protected function parse_counter_prop(string $value, int $default) + { + $ident = self::CSS_IDENTIFIER; + $integer = self::CSS_INTEGER; + $pattern = "/($ident)(?:\s+($integer))?/"; + + if (!preg_match_all($pattern, $value, $matches, PREG_SET_ORDER)) { + return "none"; + } + + $counters = []; + + foreach ($matches as $match) { + $counter = $match[1]; + $value = isset($match[2]) ? (int) $match[2] : $default; + $counters[$counter] = $value; + } + + return $counters; + } + + /** + * @param string $computed + * @return array|string + * + * @link https://www.w3.org/TR/CSS21/generate.html#propdef-counter-increment + */ + protected function _get_counter_increment($computed) + { + if ($computed === "none") { + return $computed; + } + + return $this->parse_counter_prop($computed, 1); + } + + /** + * @param string $computed + * @return array|string + * + * @link https://www.w3.org/TR/CSS21/generate.html#propdef-counter-reset + */ + protected function _get_counter_reset($computed) + { + if ($computed === "none") { + return $computed; + } + + return $this->parse_counter_prop($computed, 0); + } + + /** + * @param string $computed + * @return string[]|string + * + * @link https://www.w3.org/TR/CSS21/generate.html#propdef-content + */ + protected function _get_content($computed) + { + if ($computed === "normal" || $computed === "none") { + return $computed; + } + + return $this->parse_property_value($computed); + } + + /*==============================*/ + + /** + * Parse a property value into its components. + * + * @param string $value + * + * @return string[] + */ + protected function parse_property_value(string $value): array + { + $ident = self::CSS_IDENTIFIER; + $number = self::CSS_NUMBER; + + $pattern = "/\n" . + "\s* \" ( (?:[^\"]|\\\\[\"])* ) (?munge_color($val) + : $val; + + if ($munged_color === null) { + return null; + } + + return \is_array($munged_color) ? $munged_color["hex"] : $munged_color; + } + + /** + * @param string $val + * @return int|null + */ + protected function compute_integer(string $val): ?int + { + $integer = self::CSS_INTEGER; + return preg_match("/^$integer$/", $val) + ? (int) $val + : null; + } + + /** + * @param string $val + * @return float|null + */ + protected function compute_length(string $val): ?float + { + return mb_strpos($val, "%") === false + ? $this->single_length_in_pt($val) + : null; + } + + /** + * @param string $val + * @return float|null + */ + protected function compute_length_positive(string $val): ?float + { + $computed = $this->compute_length($val); + return $computed !== null && $computed >= 0 ? $computed : null; + } + + /** + * @param string $val + * @return float|string|null + */ + protected function compute_length_percentage(string $val) + { + // Compute with a fixed ref size to decide whether percentage values + // are valid + $computed = $this->single_length_in_pt($val, 12); + + if ($computed === null) { + return null; + } + + // Retain valid percentage declarations + return mb_strpos($val, "%") === false ? $computed : $val; + } + + /** + * @param string $val + * @return float|string|null + */ + protected function compute_length_percentage_positive(string $val) + { + // Compute with a fixed ref size to decide whether percentage values + // are valid + $computed = $this->single_length_in_pt($val, 12); + + if ($computed === null || $computed < 0) { + return null; + } + + // Retain valid percentage declarations + return mb_strpos($val, "%") === false ? $computed : $val; + } + + /** + * @param string $val + * @param string $style_prop The corresponding border-/outline-style property. + * + * @return float|null + * + * @link https://www.w3.org/TR/css-backgrounds-3/#typedef-line-width + */ + protected function compute_line_width(string $val, string $style_prop): ?float + { + // Border-width keywords + if ($val === "thin") { + $computed = 0.5; + } elseif ($val === "medium") { + $computed = 1.5; + } elseif ($val === "thick") { + $computed = 2.5; + } else { + $computed = $this->compute_length_positive($val); + } + + if ($computed === null) { + return null; + } + + // Computed width is 0 if the line style is `none` or `hidden` + // https://www.w3.org/TR/css-backgrounds-3/#border-width + // https://www.w3.org/TR/css-ui-4/#outline-width + $lineStyle = $this->__get($style_prop); + $hasLineStyle = $lineStyle !== "none" && $lineStyle !== "hidden"; + + return $hasLineStyle ? $computed : 0.0; + } + + /** + * @param string $val + * @return string|null + */ + protected function compute_border_style(string $val): ?string + { + return \in_array($val, self::BORDER_STYLES, true) ? $val : null; + } + + /** + * Parse a property value with 1 to 4 components into 4 values, as required + * by shorthand properties such as `margin`, `padding`, and `border-radius`. + * + * @param string $prop The shorthand property with exactly 4 sub-properties to handle. + * @param string $value The property value to parse. + * + * @return string[] + */ + protected function set_quad_shorthand(string $prop, string $value): array + { + $v = $this->parse_property_value($value); + + switch (\count($v)) { + case 1: + $values = [$v[0], $v[0], $v[0], $v[0]]; + break; + case 2: + $values = [$v[0], $v[1], $v[0], $v[1]]; + break; + case 3: + $values = [$v[0], $v[1], $v[2], $v[1]]; + break; + case 4: + $values = [$v[0], $v[1], $v[2], $v[3]]; + break; + default: + return []; + } + + return array_combine(self::$_props_shorthand[$prop], $values); + } + + /*======================*/ + + /** + * @link https://www.w3.org/TR/CSS21/visuren.html#display-prop + */ + protected function _compute_display(string $val) + { + // Make sure that common valid, but unsupported display types have an + // appropriate fallback display type + switch ($val) { + case "flow-root": + case "flex": + case "grid": + case "table-caption": + $val = "block"; + break; + case "inline-flex": + case "inline-grid": + $val = "inline-block"; + break; + } + + if (!isset(self::$valid_display_types[$val])) { + return null; + } + + // https://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo + if ($this->is_in_flow()) { + return $val; + } else { + switch ($val) { + case "inline": + case "inline-block": + // case "table-row-group": + // case "table-header-group": + // case "table-footer-group": + // case "table-row": + // case "table-cell": + // case "table-column-group": + // case "table-column": + // case "table-caption": + return "block"; + case "inline-table": + return "table"; + default: + return $val; + } + } + } + + /** + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-color + */ + protected function _compute_color(string $color) + { + return $this->compute_color_value($color); + } + + /** + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-color + */ + protected function _compute_background_color(string $color) + { + return $this->compute_color_value($color); + } + + /** + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-image + */ + protected function _compute_background_image(string $val) + { + $parsed_val = $this->_stylesheet->resolve_url($val); + + if ($parsed_val === "none") { + return "none"; + } else { + return "url($parsed_val)"; + } + } + + /** + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat + */ + protected function _compute_background_repeat(string $val) + { + $keywords = ["repeat", "repeat-x", "repeat-y", "no-repeat"]; + return \in_array($val, $keywords, true) ? $val : null; + } + + /** + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment + */ + protected function _compute_background_attachment(string $val) + { + $keywords = ["scroll", "fixed"]; + return \in_array($val, $keywords, true) ? $val : null; + } + + /** + * @link https://www.w3.org/TR/CSS21/colors.html#propdef-background-position + */ + protected function _compute_background_position(string $val) + { + $parts = preg_split("/\s+/", $val); + + if (\count($parts) > 2) { + return null; + } + + switch ($parts[0]) { + case "left": + $x = "0%"; + break; + + case "right": + $x = "100%"; + break; + + case "top": + $y = "0%"; + break; + + case "bottom": + $y = "100%"; + break; + + case "center": + $x = "50%"; + $y = "50%"; + break; + + default: + $x = $parts[0]; + break; + } + + if (isset($parts[1])) { + switch ($parts[1]) { + case "left": + $x = "0%"; + break; + + case "right": + $x = "100%"; + break; + + case "top": + $y = "0%"; + break; + + case "bottom": + $y = "100%"; + break; + + case "center": + if ($parts[0] === "left" || $parts[0] === "right" || $parts[0] === "center") { + $y = "50%"; + } else { + $x = "50%"; + } + break; + + default: + $y = $parts[1]; + break; + } + } else { + $y = "50%"; + } + + if (!isset($x)) { + $x = "0%"; + } + + if (!isset($y)) { + $y = "0%"; + } + + return [$x, $y]; + } + + /** + * Compute `background-size`. + * + * Computes to one of the following values: + * * `cover` + * * `contain` + * * `[width, height]`, each being a length, percentage, or `auto` + * + * @link https://www.w3.org/TR/css-backgrounds-3/#background-size + */ + protected function _compute_background_size(string $val) + { + if ($val === "cover" || $val === "contain") { + return $val; + } + + $parts = preg_split("/\s+/", $val); + + if (\count($parts) > 2) { + return null; + } + + $width = $parts[0]; + if ($width !== "auto") { + $width = $this->compute_length_percentage_positive($width); + } + + $height = $parts[1] ?? "auto"; + if ($height !== "auto") { + $height = $this->compute_length_percentage_positive($height); + } + + if ($width === null || $height === null) { + return null; + } + + return [$width, $height]; + } + + /** + * @link https://www.w3.org/TR/css-backgrounds-3/#propdef-background + */ + protected function _set_background(string $value): array + { + $components = $this->parse_property_value($value); + $props = []; + $pos_size = []; + + foreach ($components as $val) { + if ($val === "none" || mb_substr($val, 0, 4) === "url(") { + $props["background_image"] = $val; + } elseif ($val === "scroll" || $val === "fixed") { + $props["background_attachment"] = $val; + } elseif ($val === "repeat" || $val === "repeat-x" || $val === "repeat-y" || $val === "no-repeat") { + $props["background_repeat"] = $val; + } elseif ($this->is_color_value($val)) { + $props["background_color"] = $val; + } else { + $pos_size[] = $val; + } + } + + if (\count($pos_size)) { + // Split value list at "/" + $index = array_search("/", $pos_size, true); + + if ($index !== false) { + $pos = \array_slice($pos_size, 0, $index); + $size = \array_slice($pos_size, $index + 1); + } else { + $pos = $pos_size; + $size = []; + } + + $props["background_position"] = implode(" ", $pos); + + if (\count($size)) { + $props["background_size"] = implode(" ", $size); + } + } + + return $props; + } + + /** + * @link https://www.w3.org/TR/CSS21/fonts.html#propdef-font-size + */ + protected function _compute_font_size(string $size) + { + $parent_font_size = isset($this->parent_style) + ? $this->parent_style->__get("font_size") + : self::$default_font_size; + + switch ($size) { + case "xx-small": + case "x-small": + case "small": + case "medium": + case "large": + case "x-large": + case "xx-large": + $fs = self::$default_font_size * self::$font_size_keywords[$size]; + break; + + case "smaller": + $fs = 8 / 9 * $parent_font_size; + break; + + case "larger": + $fs = 6 / 5 * $parent_font_size; + break; + + default: + $fs = $this->single_length_in_pt($size, $parent_font_size, $parent_font_size); + break; + } + + return $fs; + } + + /** + * @link https://www.w3.org/TR/CSS21/fonts.html#font-boldness + */ + protected function _compute_font_weight(string $weight) + { + $computed_weight = $weight; + + if ($weight === "bolder") { + //TODO: One font weight heavier than the parent element (among the available weights of the font). + $computed_weight = "bold"; + } elseif ($weight === "lighter") { + //TODO: One font weight lighter than the parent element (among the available weights of the font). + $computed_weight = "normal"; + } + + return $computed_weight; + } + + /** + * Handle the `font` shorthand property. + * + * `[ font-style || font-variant || font-weight ] font-size [ / line-height ] font-family` + * + * @link https://www.w3.org/TR/CSS21/fonts.html#font-shorthand + */ + protected function _set_font(string $value): array + { + $components = $this->parse_property_value($value); + $props = []; + + $number = self::CSS_NUMBER; + $unit = "pt|px|pc|rem|em|ex|in|cm|mm|%"; + $sizePattern = "/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|$number(?:$unit))$/"; + $sizeIndex = null; + + // Find index of font-size to split the component list + foreach ($components as $i => $val) { + if (preg_match($sizePattern, $val)) { + $sizeIndex = $i; + $props["font_size"] = $val; + break; + } + } + + // `font-size` is mandatory + if ($sizeIndex === null) { + return []; + } + + // `font-style`, `font-variant`, `font-weight` in any order + $styleVariantWeight = \array_slice($components, 0, $sizeIndex); + $stylePattern = "/^(italic|oblique)$/"; + $variantPattern = "/^(small-caps)$/"; + $weightPattern = "/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900)$/"; + + if (\count($styleVariantWeight) > 3) { + return []; + } + + foreach ($styleVariantWeight as $val) { + if ($val === "normal") { + // Ignore any `normal` value, as it is valid and the initial + // value for all three properties + } elseif (!isset($props["font_style"]) && preg_match($stylePattern, $val)) { + $props["font_style"] = $val; + } elseif (!isset($props["font_variant"]) && preg_match($variantPattern, $val)) { + $props["font_variant"] = $val; + } elseif (!isset($props["font_weight"]) && preg_match($weightPattern, $val)) { + $props["font_weight"] = $val; + } else { + // Duplicates and other values disallowed here + return []; + } + } + + // Optional slash + `line-height` followed by mandatory `font-family` + $lineFamily = \array_slice($components, $sizeIndex + 1); + $hasLineHeight = $lineFamily !== [] && $lineFamily[0] === "/"; + $lineHeight = $hasLineHeight ? \array_slice($lineFamily, 1, 1) : []; + $fontFamily = $hasLineHeight ? \array_slice($lineFamily, 2) : $lineFamily; + $lineHeightPattern = "/^(normal|$number(?:$unit)?)$/"; + + // Missing `font-family` or `line-height` after slash + if ($fontFamily === [] + || ($hasLineHeight && !preg_match($lineHeightPattern, $lineHeight[0])) + ) { + return []; + } + + if ($hasLineHeight) { + $props["line_height"] = $lineHeight[0]; + } + + $props["font_family"] = implode("", $fontFamily); + + return $props; + } + + /** + * Compute `text-align`. + * + * If no alignment is set on the element and the direction is rtl then + * the property is set to "right", otherwise it is set to "left". + * + * @link https://www.w3.org/TR/CSS21/text.html#propdef-text-align + */ + protected function _compute_text_align(string $val) + { + $alignment = $val; + if ($alignment === "") { + $alignment = "left"; + if ($this->__get("direction") === "rtl") { + $alignment = "right"; + } + } + + if (!\in_array($alignment, self::TEXT_ALIGN_KEYWORDS, true)) { + return null; + } + + return $alignment; + } + + /** + * @link https://www.w3.org/TR/css-text-4/#word-spacing-property + */ + protected function _compute_word_spacing(string $val) + { + if ($val === "normal") { + return 0.0; + } + + return $this->compute_length_percentage($val); + } + + /** + * @link https://www.w3.org/TR/css-text-4/#letter-spacing-property + */ + protected function _compute_letter_spacing(string $val) + { + if ($val === "normal") { + return 0.0; + } + + return $this->compute_length_percentage($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-line-height + */ + protected function _compute_line_height(string $val) + { + if ($val === "normal") { + return $val; + } + + // Compute number values to string and lengths to float (in pt) + if (is_numeric($val)) { + return (string) $val; + } + + $font_size = $this->__get("font_size"); + $computed = $this->single_length_in_pt($val, $font_size); + return $computed !== null && $computed >= 0 ? $computed : null; + } + + /** + * @link https://www.w3.org/TR/css-text-3/#text-indent-property + */ + protected function _compute_text_indent(string $val) + { + return $this->compute_length_percentage($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/page.html#propdef-page-break-before + */ + protected function _compute_page_break_before(string $break) + { + if ($break === "left" || $break === "right") { + $break = "always"; + } + + return $break; + } + + /** + * @link https://www.w3.org/TR/CSS21/page.html#propdef-page-break-after + */ + protected function _compute_page_break_after(string $break) + { + if ($break === "left" || $break === "right") { + $break = "always"; + } + + return $break; + } + + /** + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-width + */ + protected function _compute_width(string $val) + { + if ($val === "auto") { + return $val; + } + + return $this->compute_length_percentage_positive($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-height + */ + protected function _compute_height(string $val) + { + if ($val === "auto") { + return $val; + } + + return $this->compute_length_percentage_positive($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-min-width + */ + protected function _compute_min_width(string $val) + { + // Legacy support for `none`, not covered by spec + if ($val === "auto" || $val === "none") { + return "auto"; + } + + return $this->compute_length_percentage_positive($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-min-height + */ + protected function _compute_min_height(string $val) + { + // Legacy support for `none`, not covered by spec + if ($val === "auto" || $val === "none") { + return "auto"; + } + + return $this->compute_length_percentage_positive($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-max-width + */ + protected function _compute_max_width(string $val) + { + // Legacy support for `auto`, not covered by spec + if ($val === "none" || $val === "auto") { + return "none"; + } + + return $this->compute_length_percentage_positive($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/visudet.html#propdef-max-height + */ + protected function _compute_max_height(string $val) + { + // Legacy support for `auto`, not covered by spec + if ($val === "none" || $val === "auto") { + return "none"; + } + + return $this->compute_length_percentage_positive($val); + } + + /** + * @link https://www.w3.org/TR/css-position-3/#inset-properties + * @link https://www.w3.org/TR/css-position-3/#propdef-inset + */ + protected function _set_inset(string $val): array + { + return $this->set_quad_shorthand("inset", $val); + } + + /** + * @param string $val + * @return float|string|null + */ + protected function compute_box_inset(string $val) + { + if ($val === "auto") { + return $val; + } + + return $this->compute_length_percentage($val); + } + + protected function _compute_top(string $val) + { + return $this->compute_box_inset($val); + } + + protected function _compute_right(string $val) + { + return $this->compute_box_inset($val); + } + + protected function _compute_bottom(string $val) + { + return $this->compute_box_inset($val); + } + + protected function _compute_left(string $val) + { + return $this->compute_box_inset($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/box.html#margin-properties + * @link https://www.w3.org/TR/CSS21/box.html#propdef-margin + */ + protected function _set_margin(string $val): array + { + return $this->set_quad_shorthand("margin", $val); + } + + /** + * @param string $val + * @return float|string|null + */ + protected function compute_margin(string $val) + { + // Legacy support for `none` keyword, not covered by spec + if ($val === "none") { + return 0.0; + } + + if ($val === "auto") { + return $val; + } + + return $this->compute_length_percentage($val); + } + + protected function _compute_margin_top(string $val) + { + return $this->compute_margin($val); + } + + protected function _compute_margin_right(string $val) + { + return $this->compute_margin($val); + } + + protected function _compute_margin_bottom(string $val) + { + return $this->compute_margin($val); + } + + protected function _compute_margin_left(string $val) + { + return $this->compute_margin($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/box.html#padding-properties + * @link https://www.w3.org/TR/CSS21/box.html#propdef-padding + */ + protected function _set_padding(string $val): array + { + return $this->set_quad_shorthand("padding", $val); + } + + /** + * @param string $val + * @return float|string|null + */ + protected function compute_padding(string $val) + { + // Legacy support for `none` keyword, not covered by spec + if ($val === "none") { + return 0.0; + } + + return $this->compute_length_percentage_positive($val); + } + + protected function _compute_padding_top(string $val) + { + return $this->compute_padding($val); + } + + protected function _compute_padding_right(string $val) + { + return $this->compute_padding($val); + } + + protected function _compute_padding_bottom(string $val) + { + return $this->compute_padding($val); + } + + protected function _compute_padding_left(string $val) + { + return $this->compute_padding($val); + } + + /** + * @param string $value `width || style || color` + * @param string[] $styles The list of border styles to accept. + * + * @return array Array of `[width, style, color]`, or `null` if the declaration is invalid. + */ + protected function parse_border_side(string $value, array $styles = self::BORDER_STYLES): ?array + { + $components = $this->parse_property_value($value); + $width = null; + $style = null; + $color = null; + + foreach ($components as $val) { + if ($style === null && \in_array($val, $styles, true)) { + $style = $val; + } elseif ($color === null && $this->is_color_value($val)) { + $color = $val; + } elseif ($width === null) { + // Assume width + $width = $val; + } else { + // Duplicates are not allowed + return null; + } + } + + return [$width, $style, $color]; + } + + /** + * @link https://www.w3.org/TR/CSS21/box.html#border-properties + * @link https://www.w3.org/TR/CSS21/box.html#propdef-border + */ + protected function _set_border(string $value): array + { + $values = $this->parse_border_side($value); + + if ($values === null) { + return []; + } + + return array_merge( + array_combine(self::$_props_shorthand["border_top"], $values), + array_combine(self::$_props_shorthand["border_right"], $values), + array_combine(self::$_props_shorthand["border_bottom"], $values), + array_combine(self::$_props_shorthand["border_left"], $values) + ); + } + + /** + * @param string $prop + * @param string $value + * @return array + */ + protected function set_border_side(string $prop, string $value): array + { + $values = $this->parse_border_side($value); + + if ($values === null) { + return []; + } + + return array_combine(self::$_props_shorthand[$prop], $values); + } + + protected function _set_border_top(string $val): array + { + return $this->set_border_side("border_top", $val); + } + + protected function _set_border_right(string $val): array + { + return $this->set_border_side("border_right", $val); + } + + protected function _set_border_bottom(string $val): array + { + return $this->set_border_side("border_bottom", $val); + } + + protected function _set_border_left(string $val): array + { + return $this->set_border_side("border_left", $val); + } + + /** + * @link https://www.w3.org/TR/CSS21/box.html#propdef-border-color + */ + protected function _set_border_color(string $val): array + { + return $this->set_quad_shorthand("border_color", $val); + } + + protected function _compute_border_top_color(string $val) + { + return $this->compute_color_value($val); + } + + protected function _compute_border_right_color(string $val) + { + return $this->compute_color_value($val); + } + + protected function _compute_border_bottom_color(string $val) + { + return $this->compute_color_value($val); + } + + protected function _compute_border_left_color(string $val) + { + return $this->compute_color_value($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/box.html#propdef-border-style + */ + protected function _set_border_style(string $val): array + { + return $this->set_quad_shorthand("border_style", $val); + } + + protected function _compute_border_top_style(string $val) + { + return $this->compute_border_style($val); + } + + protected function _compute_border_right_style(string $val) + { + return $this->compute_border_style($val); + } + + protected function _compute_border_bottom_style(string $val) + { + return $this->compute_border_style($val); + } + + protected function _compute_border_left_style(string $val) + { + return $this->compute_border_style($val); + } + + /** + * @link https://www.w3.org/TR/CSS21/box.html#propdef-border-width + */ + protected function _set_border_width(string $val): array + { + return $this->set_quad_shorthand("border_width", $val); + } + + protected function _compute_border_top_width(string $val) + { + return $this->compute_line_width($val, "border_top_style"); + } + + protected function _compute_border_right_width(string $val) + { + return $this->compute_line_width($val, "border_right_style"); + } + + protected function _compute_border_bottom_width(string $val) + { + return $this->compute_line_width($val, "border_bottom_style"); + } + + protected function _compute_border_left_width(string $val) + { + return $this->compute_line_width($val, "border_left_style"); + } + + /** + * @link https://www.w3.org/TR/css-backgrounds-3/#corners + * @link https://www.w3.org/TR/css-backgrounds-3/#propdef-border-radius + */ + protected function _set_border_radius(string $val): array + { + return $this->set_quad_shorthand("border_radius", $val); + } + + protected function _compute_border_top_left_radius(string $val) + { + return $this->compute_length_percentage_positive($val); + } + + protected function _compute_border_top_right_radius(string $val) + { + return $this->compute_length_percentage_positive($val); + } + + protected function _compute_border_bottom_right_radius(string $val) + { + return $this->compute_length_percentage_positive($val); + } + + protected function _compute_border_bottom_left_radius(string $val) + { + return $this->compute_length_percentage_positive($val); + } + + /** + * @link https://www.w3.org/TR/css-ui-4/#outline-props + * @link https://www.w3.org/TR/css-ui-4/#propdef-outline + */ + protected function _set_outline(string $value): array + { + $values = $this->parse_border_side($value, self::OUTLINE_STYLES); + + if ($values === null) { + return []; + } + + return array_combine(self::$_props_shorthand["outline"], $values); + } + + protected function _compute_outline_color(string $val) + { + return $this->compute_color_value($val); + } + + protected function _compute_outline_style(string $val) + { + return \in_array($val, self::OUTLINE_STYLES, true) ? $val : null; + } + + protected function _compute_outline_width(string $val) + { + return $this->compute_line_width($val, "outline_style"); + } + + /** + * @link https://www.w3.org/TR/css-ui-4/#propdef-outline-offset + */ + protected function _compute_outline_offset(string $val) + { + return $this->compute_length($val); + } + + /** + * Compute `border-spacing` to two lengths of the form + * `[horizontal, vertical]`. + * + * @link https://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing + */ + protected function _compute_border_spacing(string $val) + { + $parts = preg_split("/\s+/", $val); + + if (\count($parts) > 2) { + return null; + } + + $h = $this->compute_length_positive($parts[0]); + $v = isset($parts[1]) + ? $this->compute_length_positive($parts[1]) + : $h; + + if ($h === null || $v === null) { + return null; + } + + return [$h, $v]; + } + + /** + * @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image + */ + protected function _compute_list_style_image(string $val) + { + $parsed_val = $this->_stylesheet->resolve_url($val); + + if ($parsed_val === "none") { + return "none"; + } else { + return "url($parsed_val)"; + } + } + + /** + * @link https://www.w3.org/TR/CSS21/generate.html#propdef-list-style + */ + protected function _set_list_style(string $value): array + { + static $positions = ["inside", "outside"]; + static $types = [ + "disc", "circle", "square", + "decimal-leading-zero", "decimal", "1", + "lower-roman", "upper-roman", "a", "A", + "lower-greek", + "lower-latin", "upper-latin", + "lower-alpha", "upper-alpha", + "armenian", "georgian", "hebrew", + "cjk-ideographic", "hiragana", "katakana", + "hiragana-iroha", "katakana-iroha", "none" + ]; + + $components = $this->parse_property_value($value); + $props = []; + + foreach ($components as $val) { + /* https://www.w3.org/TR/CSS21/generate.html#list-style + * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none' + */ + if ($val === "none") { + $props["list_style_type"] = $val; + $props["list_style_image"] = $val; + continue; + } + + //On setting or merging or inheriting list_style_image as well as list_style_type, + //and url exists, then url has precedence, otherwise fall back to list_style_type + //Firefox is wrong here (list_style_image gets overwritten on explicit list_style_type) + //Internet Explorer 7/8 and dompdf is right. + + if (mb_substr($val, 0, 4) === "url(") { + $props["list_style_image"] = $val; + continue; + } + + if (\in_array($val, $types, true)) { + $props["list_style_type"] = $val; + } elseif (\in_array($val, $positions, true)) { + $props["list_style_position"] = $val; + } + } + + return $props; + } + + /** + * @link https://www.w3.org/TR/css-page-3/#page-size-prop + */ + protected function _compute_size(string $val) + { + if ($val === "auto") { + return $val; + } + + $parts = $this->parse_property_value($val); + $count = \count($parts); + + if ($count === 0 || $count > 3) { + return null; + } + + $size = null; + $orientation = null; + $lengths = []; + + foreach ($parts as $part) { + if ($size === null && isset(CPDF::$PAPER_SIZES[$part])) { + $size = $part; + } elseif ($orientation === null && ($part === "portrait" || $part === "landscape")) { + $orientation = $part; + } else { + $lengths[] = $part; + } + } + + if ($size !== null && $lengths !== []) { + return null; + } + + if ($size !== null) { + // Standard paper size + [$l1, $l2] = \array_slice(CPDF::$PAPER_SIZES[$size], 2, 2); + } elseif ($lengths === []) { + // Orientation only, use default paper size + $dims = $this->_stylesheet->get_dompdf()->getPaperSize(); + [$l1, $l2] = \array_slice($dims, 2, 2); + } else { + // Custom paper size + $l1 = $this->compute_length_positive($lengths[0]); + $l2 = isset($lengths[1]) ? $this->compute_length_positive($lengths[1]) : $l1; + + if ($l1 === null || $l2 === null) { + return null; + } + } + + if (($orientation === "portrait" && $l1 > $l2) + || ($orientation === "landscape" && $l2 > $l1) + ) { + return [$l2, $l1]; + } + + return [$l1, $l2]; + } + + /** + * @param string $computed + * @return array + * + * @link https://www.w3.org/TR/css-transforms-1/#transform-property + */ + protected function _get_transform($computed) + { + //TODO: should be handled in setter (lengths set to absolute) + + $number = "\s*([^,\s]+)\s*"; + $tr_value = "\s*([^,\s]+)\s*"; + $angle = "\s*([^,\s]+(?:deg|rad)?)\s*"; + + if (!preg_match_all("/[a-z]+\([^\)]+\)/i", $computed, $parts, PREG_SET_ORDER)) { + return []; + } + + $functions = [ + //"matrix" => "\($number,$number,$number,$number,$number,$number\)", + + "translate" => "\($tr_value(?:,$tr_value)?\)", + "translateX" => "\($tr_value\)", + "translateY" => "\($tr_value\)", + + "scale" => "\($number(?:,$number)?\)", + "scaleX" => "\($number\)", + "scaleY" => "\($number\)", + + "rotate" => "\($angle\)", + + "skew" => "\($angle(?:,$angle)?\)", + "skewX" => "\($angle\)", + "skewY" => "\($angle\)", + ]; + + $transforms = []; + + foreach ($parts as $part) { + $t = $part[0]; + + foreach ($functions as $name => $pattern) { + if (preg_match("/$name\s*$pattern/i", $t, $matches)) { + $values = \array_slice($matches, 1); + + switch ($name) { + // units + case "rotate": + case "skew": + case "skewX": + case "skewY": + + foreach ($values as $i => $value) { + if (strpos($value, "rad")) { + $values[$i] = rad2deg((float) $value); + } else { + $values[$i] = (float) $value; + } + } + + switch ($name) { + case "skew": + if (!isset($values[1])) { + $values[1] = 0; + } + break; + case "skewX": + $name = "skew"; + $values = [$values[0], 0]; + break; + case "skewY": + $name = "skew"; + $values = [0, $values[0]]; + break; + } + break; + + // units + case "translate": + $values[0] = $this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)); + + if (isset($values[1])) { + $values[1] = $this->length_in_pt($values[1], (float)$this->length_in_pt($this->height)); + } else { + $values[1] = 0; + } + break; + + case "translateX": + $name = "translate"; + $values = [$this->length_in_pt($values[0], (float)$this->length_in_pt($this->width)), 0]; + break; + + case "translateY": + $name = "translate"; + $values = [0, $this->length_in_pt($values[0], (float)$this->length_in_pt($this->height))]; + break; + + // units + case "scale": + if (!isset($values[1])) { + $values[1] = $values[0]; + } + break; + + case "scaleX": + $name = "scale"; + $values = [$values[0], 1.0]; + break; + + case "scaleY": + $name = "scale"; + $values = [1.0, $values[0]]; + break; + } + + $transforms[] = [ + $name, + $values, + ]; + } + } + } + + return $transforms; + } + + /** + * @param string $computed + * @return array + * + * @link https://www.w3.org/TR/css-transforms-1/#transform-origin-property + */ + protected function _get_transform_origin($computed) + { + //TODO: should be handled in setter + + $values = preg_split("/\s+/", $computed); + + $values = array_map(function ($value) { + if (\in_array($value, ["top", "left"], true)) { + return 0; + } elseif (\in_array($value, ["bottom", "right"], true)) { + return "100%"; + } else { + return $value; + } + }, $values); + + if (!isset($values[1])) { + $values[1] = $values[0]; + } + + return $values; + } + + /** + * @param string $val + * @return string|null + */ + protected function parse_image_resolution(string $val): ?string + { + // If exif data could be get: + // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/'; + + $re = '/^\s*(\d+|normal|auto)\s*$/'; + + if (!preg_match($re, $val, $matches)) { + return null; + } + + return $matches[1]; + } + + /** + * auto | normal | dpi + */ + protected function _compute_background_image_resolution(string $val) + { + return $this->parse_image_resolution($val); + } + + /** + * auto | normal | dpi + */ + protected function _compute_image_resolution(string $val) + { + return $this->parse_image_resolution($val); + } + + /** + * @link https://www.w3.org/TR/css-break-3/#propdef-orphans + */ + protected function _compute_orphans(string $val) + { + return $this->compute_integer($val); + } + + /** + * @link https://www.w3.org/TR/css-break-3/#propdef-widows + */ + protected function _compute_widows(string $val) + { + return $this->compute_integer($val); + } + + /** + * @link https://www.w3.org/TR/css-color-4/#propdef-opacity + */ + protected function _compute_opacity(string $val) + { + $number = self::CSS_NUMBER; + $pattern = "/^($number)(%?)$/"; + + if (!preg_match($pattern, $val, $matches)) { + return null; + } + + $v = (float) $matches[1]; + $percent = $matches[2] === "%"; + $opacity = $percent ? ($v / 100) : $v; + + return max(0.0, min($opacity, 1.0)); + } + + /** + * @link https://www.w3.org/TR/CSS21//visuren.html#propdef-z-index + */ + protected function _compute_z_index(string $val) + { + if ($val === "auto") { + return $val; + } + + return $this->compute_integer($val); + } + + /** + * @param FontMetrics $fontMetrics + * @return $this + */ + public function setFontMetrics(FontMetrics $fontMetrics) + { + $this->fontMetrics = $fontMetrics; + return $this; + } + + /** + * @return FontMetrics + */ + public function getFontMetrics() + { + return $this->fontMetrics; + } + + /** + * Generate a string representation of the Style + * + * This dumps the entire property array into a string via print_r. Useful + * for debugging. + * + * @return string + */ + /*DEBUGCSS print: see below additional debugging util*/ + public function __toString(): string + { + $parent_font_size = $this->parent_style + ? $this->parent_style->font_size + : self::$default_font_size; + + return print_r(array_merge(["parent_font_size" => $parent_font_size], + $this->_props), true); + } + + /*DEBUGCSS*/ + public function debug_print(): void + { + $parent_font_size = $this->parent_style + ? $this->parent_style->font_size + : self::$default_font_size; + + print " parent_font_size:" . $parent_font_size . ";\n"; + print " Props [\n"; + print " specified [\n"; + foreach ($this->_props as $prop => $val) { + print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true)); + if (isset($this->_important_props[$prop])) { + print ' !important'; + } + print ";\n"; + } + print " ]\n"; + print " computed [\n"; + foreach ($this->_props_computed as $prop => $val) { + print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true)); + print ";\n"; + } + print " ]\n"; + print " cached [\n"; + foreach ($this->_props_used as $prop => $val) { + print ' ' . $prop . ': ' . preg_replace("/\r\n/", ' ', print_r($val, true)); + print ";\n"; + } + print " ]\n"; + print " ]\n"; + } +} diff --git a/library/vendor/dompdf/src/Css/Stylesheet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Stylesheet.php similarity index 83% rename from library/vendor/dompdf/src/Css/Stylesheet.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Stylesheet.php index 9d1a1ecee..20e019ab0 100644 --- a/library/vendor/dompdf/src/Css/Stylesheet.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Css/Stylesheet.php @@ -1,10 +1,7 @@ - * @author Helmut Tischer - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Css; @@ -66,13 +63,13 @@ class Stylesheet * not support user stylesheets, and user agent stylesheets can not include * important declarations. */ - private static $_stylesheet_origins = array( + private static $_stylesheet_origins = [ self::ORIG_UA => 0x00000000, // user agent declarations self::ORIG_USER => 0x10000000, // user normal declarations self::ORIG_AUTHOR => 0x30000000, // author normal declarations - ); + ]; - /* + /** * Non-CSS presentational hints (i.e. HTML 4 attributes) are handled as if added * to the beginning of an author stylesheet, i.e. anything in author stylesheets * should override them. @@ -99,7 +96,7 @@ class Stylesheet * * @var string */ - private $_protocol; + private $_protocol = ""; /** * Base hostname of the document being parsed @@ -107,7 +104,7 @@ class Stylesheet * * @var string */ - private $_base_host; + private $_base_host = ""; /** * Base path of the document being parsed @@ -115,7 +112,7 @@ class Stylesheet * * @var string */ - private $_base_path; + private $_base_path = ""; /** * The styles defined by @page rules @@ -152,8 +149,8 @@ class Stylesheet * (Previous version $ACCEPTED_MEDIA_TYPES = $ACCEPTED_GENERIC_MEDIA_TYPES + $ACCEPTED_DEFAULT_MEDIA_TYPE) */ static $ACCEPTED_DEFAULT_MEDIA_TYPE = "print"; - static $ACCEPTED_GENERIC_MEDIA_TYPES = array("all", "static", "visual", "bitmap", "paged", "dompdf"); - static $VALID_MEDIA_TYPES = array("all", "aural", "bitmap", "braille", "dompdf", "embossed", "handheld", "paged", "print", "projection", "screen", "speech", "static", "tty", "tv", "visual"); + static $ACCEPTED_GENERIC_MEDIA_TYPES = ["all", "static", "visual", "bitmap", "paged", "dompdf"]; + static $VALID_MEDIA_TYPES = ["all", "aural", "bitmap", "braille", "dompdf", "embossed", "handheld", "paged", "print", "projection", "screen", "speech", "static", "tty", "tv", "visual"]; /** * @var FontMetrics @@ -170,14 +167,14 @@ class Stylesheet { $this->_dompdf = $dompdf; $this->setFontMetrics($dompdf->getFontMetrics()); - $this->_styles = array(); - $this->_loaded_files = array(); + $this->_styles = []; + $this->_loaded_files = []; $script = __FILE__; - if(isset($_SERVER["SCRIPT_FILENAME"])){ + if (isset($_SERVER["SCRIPT_FILENAME"])) { $script = $_SERVER["SCRIPT_FILENAME"]; } list($this->_protocol, $this->_base_host, $this->_base_path) = Helpers::explode_url($script); - $this->_page_styles = array("base" => new Style($this)); + $this->_page_styles = ["base" => new Style($this)]; } /** @@ -185,7 +182,7 @@ class Stylesheet * * @param string $protocol */ - function set_protocol($protocol) + function set_protocol(string $protocol) { $this->_protocol = $protocol; } @@ -195,7 +192,7 @@ class Stylesheet * * @param string $host */ - function set_host($host) + function set_host(string $host) { $this->_base_host = $host; } @@ -205,7 +202,7 @@ class Stylesheet * * @param string $path */ - function set_base_path($path) + function set_base_path(string $path) { $this->_base_path = $path; } @@ -260,59 +257,32 @@ class Stylesheet return $this->_page_styles; } + /** + * Create a new Style object associated with this stylesheet + * + * @return Style + */ + function create_style(): Style + { + return new Style($this, $this->_current_origin); + } + /** * Add a new Style object to the stylesheet - * add_style() adds a new Style object to the current stylesheet, or - * merges a new Style with an existing one. + * + * The style's origin is changed to the current origin of the stylesheet. * * @param string $key the Style's selector * @param Style $style the Style to be added - * - * @throws \Dompdf\Exception */ - function add_style($key, Style $style) - { - if (!is_string($key)) { - throw new Exception("CSS rule must be keyed by a string."); - } - - if (!isset($this->_styles[$key])) { - $this->_styles[$key] = array(); - } - $new_style = clone $style; - $new_style->set_origin($this->_current_origin); - $this->_styles[$key][] = $new_style; - } - - /** - * lookup a specific Style collection - * - * lookup() returns the Style collection specified by $key, or null if the Style is - * not found. - * - * @param string $key the selector of the requested Style - * @return Style - * - * @Fixme _styles is a two dimensional array. It should produce wrong results - */ - function lookup($key) + function add_style(string $key, Style $style): void { if (!isset($this->_styles[$key])) { - return null; + $this->_styles[$key] = []; } - return $this->_styles[$key]; - } - - /** - * create a new Style object associated with this stylesheet - * - * @param Style $parent The style of this style's parent in the DOM tree - * @return Style - */ - function create_style(Style $parent = null) - { - return new Style($this, $this->_current_origin); + $style->set_origin($this->_current_origin); + $this->_styles[$key][] = $style; } /** @@ -329,7 +299,6 @@ class Stylesheet $this->_parse_css($css); } - /** * load and parse a CSS file * @@ -353,18 +322,27 @@ class Stylesheet $parsed = Helpers::parse_data_uri($file); $css = $parsed["data"]; } else { + $options = $this->_dompdf->getOptions(); + $parsed_url = Helpers::explode_url($file); + $protocol = $parsed_url["protocol"]; - list($this->_protocol, $this->_base_host, $this->_base_path, $filename) = $parsed_url; - - // Fix submitted by Nick Oostveen for aliased directory support: - if ($this->_protocol == "") { - $file = $this->_base_path . $filename; - } else { - $file = Helpers::build_url($this->_protocol, $this->_base_host, $this->_base_path, $filename); + if ($file !== $this->getDefaultStylesheet()) { + $allowed_protocols = $options->getAllowedProtocols(); + if (!array_key_exists($protocol, $allowed_protocols)) { + Helpers::record_warnings(E_USER_WARNING, "Permission denied on $file. The communication protocol is not supported.", __FILE__, __LINE__); + return; + } + foreach ($allowed_protocols[$protocol]["rules"] as $rule) { + [$result, $message] = $rule($file); + if (!$result) { + Helpers::record_warnings(E_USER_WARNING, "Error loading $file: $message", __FILE__, __LINE__); + return; + } + } } - list($css, $http_response_header) = Helpers::getFileContent($file, $this->_dompdf->getHttpContext()); + [$css, $http_response_header] = Helpers::getFileContent($file, $this->_dompdf->getHttpContext()); $good_mime_type = true; @@ -378,11 +356,12 @@ class Stylesheet } } } - - if (!$good_mime_type || $css == "") { + if (!$good_mime_type || $css === null) { Helpers::record_warnings(E_USER_WARNING, "Unable to load css file $file", __FILE__, __LINE__); return; } + + [$this->_protocol, $this->_base_host, $this->_base_path] = $parsed_url; } $this->_parse_css($css); @@ -414,7 +393,9 @@ class Stylesheet $d = min(mb_substr_count($selector, " ") + mb_substr_count($selector, ">") + - mb_substr_count($selector, "+"), 255); + mb_substr_count($selector, "+") + + mb_substr_count($selector, "~") - + mb_substr_count($selector, "~="), 255); //If a normal element name is at the beginning of the string, //a leading whitespace might have been removed on whitespace collapsing and removal @@ -422,7 +403,7 @@ class Stylesheet //this can lead to a too small specificity //see _css_selector_to_xpath - if (!in_array($selector[0], array(" ", ">", ".", "#", "+", ":", "[")) && $selector !== "*") { + if (!in_array($selector[0], [" ", ">", ".", "#", "+", "~", ":", "["]) && $selector !== "*") { $d++; } @@ -447,9 +428,8 @@ class Stylesheet * @throws Exception * @return array */ - private function _css_selector_to_xpath($selector, $first_pass = false) + private function _css_selector_to_xpath(string $selector, bool $first_pass = false): array { - // Collapse white space and strip whitespace around delimiters //$search = array("/\\s+/", "/\\s+([.>#+:])\\s+/"); //$replace = array(" ", "\\1"); @@ -459,15 +439,15 @@ class Stylesheet $query = "//"; // Will contain :before and :after - $pseudo_elements = array(); + $pseudo_elements = []; // Will contain :link, etc - $pseudo_classes = array(); + $pseudo_classes = []; // Parse the selector //$s = preg_split("/([ :>.#+])/", $selector, -1, PREG_SPLIT_DELIM_CAPTURE); - $delimiters = array(" ", ">", ".", "#", "+", ":", "[", "("); + $delimiters = [" ", ">", ".", "#", "+", "~", ":", "[", "("]; // Add an implicit * at the beginning of the selector // if it begins with an attribute selector @@ -562,20 +542,34 @@ class Stylesheet // class=".* $tok .*" and class=".* $tok" // This doesn't work because libxml only supports XPath 1.0... - //$query .= "[matches(@$attr,\"^${tok}\$|^${tok}[ ]+|[ ]+${tok}\$|[ ]+${tok}[ ]+\")]"; + //$query .= "[matches(@$attr,\"^{$tok}\$|^{$tok}[ ]+|[ ]+{$tok}\$|[ ]+{$tok}[ ]+\")]"; - // Query improvement by Michael Sheakoski : - $query .= "[contains(concat(' ', @$attr, ' '), concat(' ', '$tok', ' '))]"; + $query .= "[contains(concat(' ', normalize-space(@$attr), ' '), concat(' ', '$tok', ' '))]"; $tok = ""; break; case "+": - // All sibling elements that follow the current token + case "~": + // Next-sibling combinator + // Subsequent-sibling combinator + // https://www.w3.org/TR/selectors-3/#sibling-combinators if (mb_substr($query, -1, 1) !== "/") { $query .= "/"; } + // Tag names are case-insensitive + $tok = strtolower($tok); + + if (!$tok) { + $tok = "*"; + } + $query .= "following-sibling::$tok"; + + if ($s === "+") { + $query .= "[1]"; + } + $tok = ""; break; @@ -591,7 +585,7 @@ class Stylesheet switch ($tok) { case "first-child": - $query .= "[1]"; + $query .= "[not(preceding-sibling::*)]"; $tok = ""; break; @@ -624,16 +618,17 @@ class Stylesheet $pseudo_classes[$tok] = true; $p = $i + 1; $nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p)); + $position = $last ? "(last()-position()+1)" : "position()"; // 1 if (preg_match("/^\d+$/", $nth)) { - $condition = "position() = $nth"; + $condition = "$position = $nth"; } // odd elseif ($nth === "odd") { - $condition = "(position() mod 2) = 1"; + $condition = "($position mod 2) = 1"; } // even elseif ($nth === "even") { - $condition = "(position() mod 2) = 0"; + $condition = "($position mod 2) = 0"; } // an+b else { $condition = $this->_selector_an_plus_b($nth, $last); @@ -655,16 +650,17 @@ class Stylesheet $pseudo_classes[$tok] = true; $p = $i + 1; $nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i) - $p)); + $position = $last ? "(last()-position()+1)" : "position()"; // 1 if (preg_match("/^\d+$/", $nth)) { - $condition = "position() = $nth"; + $condition = "$position = $nth"; } // odd elseif ($nth === "odd") { - $condition = "(position() mod 2) = 1"; + $condition = "($position mod 2) = 1"; } // even elseif ($nth === "even") { - $condition = "(position() mod 2) = 0"; + $condition = "($position mod 2) = 0"; } // an+b else { $condition = $this->_selector_an_plus_b($nth, $last); @@ -757,7 +753,8 @@ class Stylesheet case "[": // Attribute selectors. All with an attribute matching the following token(s) - $attr_delimiters = array("=", "]", "~", "|", "$", "^", "*"); + // https://www.w3.org/TR/selectors-3/#attribute-selectors + $attr_delimiters = ["=", "]", "~", "|", "$", "^", "*"]; $tok_len = mb_strlen($tok); $j = 0; @@ -824,14 +821,9 @@ class Stylesheet case "~=": // FIXME: this will break if $value contains quoted strings // (e.g. [type~="a b c" "d e f"]) - $values = explode(" ", $value); - $query .= "["; - - foreach ($values as $val) { - $query .= "@$attr=\"$val\" or "; - } - - $query = rtrim($query, " or ") . "]"; + // FIXME: Don't match anything if value contains + // whitespace or is the empty string + $query .= "[contains(concat(' ', normalize-space(@$attr), ' '), concat(' ', '$value', ' '))]"; break; case "|=": @@ -889,32 +881,33 @@ class Stylesheet $query = rtrim($query, "/"); } - return array("query" => $query, "pseudo_elements" => $pseudo_elements); + return ['query' => $query, 'pseudo_elements' => $pseudo_elements]; } /** * https://github.com/tenderlove/nokogiri/blob/master/lib/nokogiri/css/xpath_visitor.rb * - * @param $expr + * @param string $expr * @param bool $last + * * @return string */ - protected function _selector_an_plus_b($expr, $last = false) + protected function _selector_an_plus_b(string $expr, bool $last = false): string { $expr = preg_replace("/\s/", "", $expr); if (!preg_match("/^(?P-?[0-9]*)?n(?P[-+]?[0-9]+)?$/", $expr, $matches)) { return "false()"; } - $a = ((isset($matches["a"]) && $matches["a"] !== "") ? intval($matches["a"]) : 1); - $b = ((isset($matches["b"]) && $matches["b"] !== "") ? intval($matches["b"]) : 0); + $a = (isset($matches["a"]) && $matches["a"] !== "") ? ($matches["a"] !== "-" ? intval($matches["a"]) : -1) : 1; + $b = (isset($matches["b"]) && $matches["b"] !== "") ? intval($matches["b"]) : 0; - $position = ($last ? "(last()-position()+1)" : "position()"); + $position = $last ? "(last()-position()+1)" : "position()"; if ($b == 0) { return "($position mod $a) = 0"; } else { - $compare = (($a < 0) ? "<=" : ">="); + $compare = ($a < 0) ? "<=" : ">="; $b2 = -$b; if ($b2 >= 0) { $b2 = "+$b2"; @@ -946,7 +939,7 @@ class Stylesheet // FIXME: this is not particularly robust... - $styles = array(); + $styles = []; $xp = new DOMXPath($tree->get_dom()); $DEBUGCSS = $this->_dompdf->getOptions()->getDebugCss(); @@ -963,7 +956,7 @@ class Stylesheet // Retrieve the nodes, limit to body for generated content //TODO: If we use a context node can we remove the leading dot? $nodes = @$xp->query('.' . $query["query"]); - if ($nodes == null) { + if ($nodes === false) { Helpers::record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__); continue; } @@ -981,7 +974,16 @@ class Stylesheet continue; } - if (($src = $this->_image($style->get_prop('content'))) !== "none") { + $content = $style->get_specified("content"); + + // Do not create non-displayed before/after pseudo elements + // https://www.w3.org/TR/CSS21/generate.html#content + // https://www.w3.org/TR/CSS21/generate.html#undisplayed-counters + if ($content === "normal" || $content === "none") { + continue; + } + + if (($src = $this->resolve_url($content)) !== "none") { $new_node = $node->ownerDocument->createElement("img_generated"); $new_node->setAttribute("src", $src); } else { @@ -1004,7 +1006,7 @@ class Stylesheet // Retrieve the nodes $nodes = @$xp->query($query["query"]); - if ($nodes == null) { + if ($nodes === false) { Helpers::record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__); continue; } @@ -1041,7 +1043,7 @@ class Stylesheet // Now create the styles and assign them to the appropriate frames. (We // iterate over the tree using an implicit FrameTree iterator.) $root_flg = false; - foreach ($tree->get_frames() as $frame) { + foreach ($tree as $frame) { // Helpers::pre_r($frame->get_node()->nodeName . ":"); if (!$root_flg && $this->_page_styles["base"]) { $style = $this->_page_styles["base"]; @@ -1052,18 +1054,15 @@ class Stylesheet // Find nearest DOMElement parent $p = $frame; while ($p = $p->get_parent()) { - if ($p->get_node()->nodeType == XML_ELEMENT_NODE) { + if ($p->get_node()->nodeType === XML_ELEMENT_NODE) { break; } } // Styles can only be applied directly to DOMElements; anonymous // frames inherit from their parent - if ($frame->get_node()->nodeType != XML_ELEMENT_NODE) { - if ($p) { - $style->inherit($p->get_style()); - } - + if ($frame->get_node()->nodeType !== XML_ELEMENT_NODE) { + $style->inherit($p ? $p->get_style() : null); $frame->set_style($style); continue; } @@ -1089,21 +1088,21 @@ class Stylesheet if (isset($styles[$id])) { /** @var array[][] $applied_styles */ - $applied_styles = $styles[$frame->get_id()]; + $applied_styles = $styles[$id]; // Sort by specificity ksort($applied_styles); if ($DEBUGCSS) { $debug_nodename = $frame->get_node()->nodeName; - print "
\n[$debug_nodename\n";
+                    print "
\n$debug_nodename [\n";
                     foreach ($applied_styles as $spec => $arr) {
-                        printf("specificity: 0x%08x\n", $spec);
+                        printf("  specificity 0x%08x\n", $spec);
                         /** @var Style $s */
                         foreach ($arr as $s) {
-                            print "[\n";
+                            print "  [\n";
                             $s->debug_print();
-                            print "]\n";
+                            print "  ]\n";
                         }
                     }
                 }
@@ -1174,32 +1173,23 @@ class Stylesheet
                 }
             }
 
-            // Inherit parent's styles if required
-            if ($p) {
-
-                if ($DEBUGCSS) {
-                    print "inherit:\n";
-                    print "[\n";
-                    $p->get_style()->debug_print();
-                    print "]\n";
-                }
-
-                $style->inherit($p->get_style());
+            // Handle inheritance
+            if ($p && $DEBUGCSS) {
+                print "  inherit [\n";
+                $p->get_style()->debug_print();
+                print "  ]\n";
             }
 
+            $style->inherit($p ? $p->get_style() : null);
+
             if ($DEBUGCSS) {
-                print "DomElementStyle:\n";
-                print "[\n";
+                print "  DomElementStyle [\n";
                 $style->debug_print();
-                print "]\n";
-                print "/$debug_nodename]\n
"; + print " ]\n"; + print "]\n
"; } - /*DEBUGCSS print: see below different print debugging method - Helpers::pre_r($frame->get_node()->nodeName . ":"); - echo "
";
-            echo $style;
-            echo "
";*/ + $style->clear_important(); $frame->set_style($style); if (!$root_flg && $this->_page_styles["base"]) { @@ -1221,7 +1211,6 @@ class Stylesheet $this->_styles[$key] = null; unset($this->_styles[$key]); } - } /** @@ -1234,15 +1223,14 @@ class Stylesheet */ private function _parse_css($str) { - $str = trim($str); // Destroy comments and remove HTML comments - $css = preg_replace(array( + $css = preg_replace([ "'/\*.*?\*/'si", "/^$/" - ), "", $str); + ], "", $str); // FIXME: handle '{' within strings, e.g. [attr="string {}"] @@ -1304,16 +1292,16 @@ class Stylesheet } elseif (!in_array($media_query, self::$VALID_MEDIA_TYPES)) { // otherwise conditionally parse the stylesheet assuming there are parseable media queries if (preg_match_all($media_query_regex, $media_query, $media_query_matches, PREG_SET_ORDER) !== false) { - $mq = array(); + $mq = []; foreach ($media_query_matches as $media_query_match) { if (empty($media_query_match[1]) === false) { $media_query_feature = strtolower($media_query_match[3]); $media_query_value = strtolower($media_query_match[2]); - $mq[] = array($media_query_feature, $media_query_value); - } else if (empty($media_query_match[4]) === false) { + $mq[] = [$media_query_feature, $media_query_value]; + } elseif (empty($media_query_match[4]) === false) { $media_query_feature = strtolower($media_query_match[5]); $media_query_value = (array_key_exists(8, $media_query_match) ? strtolower($media_query_match[8]) : null); - $mq[] = array($media_query_feature, $media_query_value); + $mq[] = [$media_query_feature, $media_query_value]; } } $this->_parse_sections($match[5], $mq); @@ -1359,6 +1347,7 @@ class Stylesheet /** @noinspection PhpMissingBreakStatementInspection */ case ":first": $key = $page_selector; + break; default: break 2; @@ -1387,57 +1376,44 @@ class Stylesheet if ($match[7] !== "") { $this->_parse_sections($match[7]); } - } } /** - * See also style.cls Style::_image(), refactoring?, works also for imported css files + * Resolve the given `url()` declaration to an absolute URL. * - * @param $val - * @return string + * @param string|null $val The declaration to resolve in the context of the stylesheet. + * @return string The resolved URL, or `none`, if the value is `none`, + * invalid, or points to a non-existent local file. */ - protected function _image($val) + public function resolve_url($val): string { $DEBUGCSS = $this->_dompdf->getOptions()->getDebugCss(); $parsed_url = "none"; - if (mb_strpos($val, "url") === false) { + if (empty($val) || $val === "none") { + $path = "none"; + } elseif (mb_strpos($val, "url") === false) { $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none } else { $val = preg_replace("/url\(\s*['\"]?([^'\")]+)['\"]?\s*\)/", "\\1", trim($val)); // Resolve the url now in the context of the current stylesheet - $parsed_url = Helpers::explode_url($val); - if ($parsed_url["protocol"] == "" && $this->get_protocol() == "") { - if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\') { - $path = $_SERVER["DOCUMENT_ROOT"] . '/'; - } else { - $path = $this->get_base_path(); - } - - $path .= $parsed_url["path"] . $parsed_url["file"]; - $path = realpath($path); - // If realpath returns FALSE then specifically state that there is no background image - // FIXME: Is this causing problems for imported CSS files? There are some './none' references when running the test cases. - if (!$path) { - $path = 'none'; - } - } else { - $path = Helpers::build_url($this->get_protocol(), - $this->get_host(), - $this->get_base_path(), - $val); + $path = Helpers::build_url($this->_protocol, + $this->_base_host, + $this->_base_path, + $val); + if ($path === null) { + $path = "none"; } } - if ($DEBUGCSS) { + $parsed_url = Helpers::explode_url($path); print "
[_image\n";
             print_r($parsed_url);
-            print $this->get_protocol() . "\n" . $this->get_base_path() . "\n" . $path . "\n";
-            print "_image]
";; + print $this->_protocol . "\n" . $this->_base_path . "\n" . $path . "\n"; + print "_image]"; } - return $path; } @@ -1477,20 +1453,19 @@ class Stylesheet // $url = str_replace(array('"',"url", "(", ")"), "", $url); // If the protocol is php, assume that we will import using file:// - // $url = Helpers::build_url($protocol == "php://" ? "file://" : $protocol, $host, $path, $url); + // $url = Helpers::build_url($protocol === "php://" ? "file://" : $protocol, $host, $path, $url); // Above does not work for subfolders and absolute urls. // Todo: As above, do we need to replace php or file to an empty protocol for local files? - $url = $this->_image($url); - - $this->load_css_file($url); + if (($url = $this->resolve_url($url)) !== "none") { + $this->load_css_file($url); + } // Restore the current base url $this->_protocol = $protocol; $this->_base_host = $host; $this->_base_path = $path; } - } /** @@ -1503,24 +1478,20 @@ class Stylesheet { $descriptors = $this->_parse_properties($str); - preg_match_all("/(url|local)\s*\([\"\']?([^\"\'\)]+)[\"\']?\)\s*(format\s*\([\"\']?([^\"\'\)]+)[\"\']?\))?/i", $descriptors->src, $src); - - $sources = array(); - $valid_sources = array(); + preg_match_all("/(url|local)\s*\(\s*[\"\']?([^\"\'\)]+)[\"\']?\s*\)\s*(format\s*\(\s*[\"\']?([^\"\'\)]+)[\"\']?\s*\))?/i", $descriptors->src, $src); + $valid_sources = []; foreach ($src[0] as $i => $value) { - $source = array( + $source = [ "local" => strtolower($src[1][$i]) === "local", "uri" => $src[2][$i], "format" => strtolower($src[4][$i]), "path" => Helpers::build_url($this->_protocol, $this->_base_host, $this->_base_path, $src[2][$i]), - ); + ]; - if (!$source["local"] && in_array($source["format"], array("", "truetype"))) { + if (!$source["local"] && in_array($source["format"], ["", "truetype"]) && $source["path"] !== null) { $valid_sources[] = $source; } - - $sources[] = $source; } // No valid sources @@ -1528,11 +1499,11 @@ class Stylesheet return; } - $style = array( + $style = [ "family" => $descriptors->get_font_family_raw(), "weight" => $descriptors->font_weight, "style" => $descriptors->font_style, - ); + ]; $this->getFontMetrics()->registerFont($style, $valid_sources[0]["path"], $this->_dompdf->getHttpContext()); } @@ -1565,14 +1536,9 @@ class Stylesheet if (preg_match("/([a-z-]+)\s*:\s*[^:]+$/i", $prop, $m)) $prop = $m[0]; }*/ + //A css property can have " ! important" appended (whitespace optional) //strip this off to decode core of the property correctly. - //Pass on in the style to allow proper handling: - //!important properties can only be overridden by other !important ones. - //$style->$prop_name = is a shortcut of $style->__set($prop_name,$value);. - //If no specific set function available, set _props["prop_name"] - //style is always copied completely, or $_props handled separately - //Therefore set a _important_props["prop_name"]=true to indicate the modifier /* Instead of short code, prefer the typical case with fast code $important = preg_match("/(.*?)!\s*important/",$prop,$match); @@ -1608,19 +1574,10 @@ class Stylesheet $prop_name = rtrim(mb_strtolower(mb_substr($prop, 0, $i))); $value = ltrim(mb_substr($prop, $i + 1)); + if ($DEBUGCSS) print $prop_name . ':=' . $value . ($important ? '!IMPORTANT' : '') . ')'; - //New style, anyway empty - //if ($important || !$style->important_get($prop_name) ) { - //$style->$prop_name = array($value,$important); - //assignment might be replaced by overloading through __set, - //and overloaded functions might check _important_props, - //therefore set _important_props first. - if ($important) { - $style->important_set($prop_name); - } - //For easier debugging, don't use overloading of assignments with __set - $style->$prop_name = $value; - //$style->props_set($prop_name, $value); + + $style->set_prop($prop_name, $value, $important, false); } if ($DEBUGCSS) print '_parse_properties]'; @@ -1633,14 +1590,12 @@ class Stylesheet * @param string $str CSS selectors and rulesets * @param array $media_queries */ - private function _parse_sections($str, $media_queries = array()) + private function _parse_sections($str, $media_queries = []) { - // Pre-process: collapse all whitespace and strip whitespace around '>', - // '.', ':', '+', '#' - - $patterns = array("/[\\s\n]+/", "/\\s+([>.:+#])\\s+/"); - $replacements = array(" ", "\\1"); - $str = preg_replace($patterns, $replacements, $str); + // Pre-process selectors: collapse all whitespace and strip whitespace + // around '>', '.', ':', '+', '~', '#' + $patterns = ["/\s+/", "/\s+([>.:+~#])\s+/"]; + $replacements = [" ", "\\1"]; $DEBUGCSS = $this->_dompdf->getOptions()->getDebugCss(); $sections = explode("}", $str); @@ -1649,10 +1604,10 @@ class Stylesheet $i = mb_strpos($sect, "{"); if ($i === false) { continue; } - //$selectors = explode(",", mb_substr($sect, 0, $i)); - $selectors = preg_split("/,(?![^\(]*\))/", mb_substr($sect, 0, $i),0, PREG_SPLIT_NO_EMPTY); if ($DEBUGCSS) print '[section'; + $selector_str = preg_replace($patterns, $replacements, mb_substr($sect, 0, $i)); + $selectors = preg_split("/,(?![^\(]*\))/", $selector_str, 0, PREG_SPLIT_NO_EMPTY); $style = $this->_parse_properties(trim(mb_substr($sect, $i + 1))); // Assign it to the selected elements @@ -1679,17 +1634,18 @@ class Stylesheet } if ($DEBUGCSS) { - print '_parse_sections]'; + print "_parse_sections]\n"; } } /** * @return string */ - public static function getDefaultStylesheet() + public function getDefaultStylesheet() { - $dir = realpath(__DIR__ . "/../.."); - return $dir . self::DEFAULT_STYLESHEET; + $options = $this->_dompdf->getOptions(); + $rootDir = realpath($options->getRootDir()); + return Helpers::build_url("file://", "", $rootDir, $rootDir . self::DEFAULT_STYLESHEET); } /** diff --git a/library/vendor/dompdf/src/Dompdf.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Dompdf.php similarity index 66% rename from library/vendor/dompdf/src/Dompdf.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Dompdf.php index 90d26ae22..6feec59a6 100644 --- a/library/vendor/dompdf/src/Dompdf.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Dompdf.php @@ -1,9 +1,7 @@ - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf; @@ -14,11 +12,10 @@ use Dompdf\Adapter\CPDF; use DOMXPath; use Dompdf\Frame\Factory; use Dompdf\Frame\FrameTree; -use HTML5_Tokenizer; -use HTML5_TreeBuilder; use Dompdf\Image\Cache; -use Dompdf\Renderer\ListBullet; use Dompdf\Css\Stylesheet; +use Dompdf\Helpers; +use Masterminds\HTML5; /** * Dompdf - PHP5 HTML to PDF renderer @@ -123,7 +120,7 @@ class Dompdf * * @var array */ - private $callbacks = array(); + private $callbacks = []; /** * Experimental caching capability @@ -149,26 +146,11 @@ class Dompdf private $basePath = ""; /** - * Protcol used to request file (file://, http://, etc) + * Protocol used to request file (file://, http://, etc) * * @var string */ - private $protocol; - - /** - * HTTP context created with stream_context_create() - * Will be used for file_get_contents - * - * @var resource - */ - private $httpContext; - - /** - * Timestamp of the script start time - * - * @var int - */ - private $startTime = null; + private $protocol = ""; /** * The system's locale @@ -178,11 +160,18 @@ class Dompdf private $systemLocale = null; /** - * Tells if the system's locale is the C standard one + * The system's mbstring internal encoding * - * @var bool + * @var string */ - private $localeStandard = false; + private $mbstringEncoding = null; + + /** + * The system's PCRE JIT configuration + * + * @var string + */ + private $pcreJit = null; /** * The default view of the PDF in the viewer @@ -196,25 +185,15 @@ class Dompdf * * @var array */ - private $defaultViewOptions = array(); + private $defaultViewOptions = []; /** - * Tells wether the DOM document is in quirksmode (experimental) + * Tells whether the DOM document is in quirksmode (experimental) * * @var bool */ private $quirksmode = false; - /** - * Protocol whitelist - * - * Protocols and PHP wrappers allowed in URLs. Full support is not - * guarantee for the protocols/wrappers contained in this array. - * - * @var array - */ - private $allowedProtocols = array(null, "", "file://", "http://", "https://"); - /** * Local file extension whitelist * @@ -222,12 +201,12 @@ class Dompdf * * @var array */ - private $allowedLocalFileExtensions = array("htm", "html"); + private $allowedLocalFileExtensions = ["htm", "html"]; /** * @var array */ - private $messages = array(); + private $messages = []; /** * @var Options @@ -245,39 +224,32 @@ class Dompdf * @var array * @deprecated */ - public static $native_fonts = array( + public static $native_fonts = [ "courier", "courier-bold", "courier-oblique", "courier-boldoblique", "helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique", "times-roman", "times-bold", "times-italic", "times-bolditalic", "symbol", "zapfdinbats" - ); + ]; /** * The list of built-in fonts * * @var array */ - public static $nativeFonts = array( + public static $nativeFonts = [ "courier", "courier-bold", "courier-oblique", "courier-boldoblique", "helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique", "times-roman", "times-bold", "times-italic", "times-bolditalic", "symbol", "zapfdinbats" - ); + ]; /** * Class constructor * - * @param array|Options $options + * @param Options|array|null $options */ public function __construct($options = null) { - mb_internal_encoding('UTF-8'); - - if (version_compare(PHP_VERSION, '7.0.0') >= 0) - { - ini_set('pcre.jit', 0); - } - if (isset($options) && $options instanceof Options) { $this->setOptions($options); } elseif (is_array($options)) { @@ -287,46 +259,62 @@ class Dompdf } $versionFile = realpath(__DIR__ . '/../VERSION'); - if (file_exists($versionFile) && ($version = file_get_contents($versionFile)) !== false && $version !== '$Format:<%h>$') { - $this->version = sprintf('dompdf %s', $version); + if (($version = file_get_contents($versionFile)) !== false) { + $version = trim($version); + if ($version !== '$Format:<%h>$') { + $this->version = sprintf('dompdf %s', $version); + } } - $this->localeStandard = sprintf('%.1f', 1.0) == '1.0'; - $this->saveLocale(); + $this->setPhpConfig(); + $this->paperSize = $this->options->getDefaultPaperSize(); $this->paperOrientation = $this->options->getDefaultPaperOrientation(); - $this->setCanvas(CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation)); - $this->setFontMetrics(new FontMetrics($this->getCanvas(), $this->getOptions())); + $this->canvas = CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation); + $this->fontMetrics = new FontMetrics($this->canvas, $this->options); $this->css = new Stylesheet($this); - $this->restoreLocale(); + $this->restorePhpConfig(); } /** - * Save the system's locale configuration and - * set the right value for numeric formatting + * Save the system's existing locale, PCRE JIT, and MBString encoding + * configuration and configure the system for Dompdf processing */ - private function saveLocale() + private function setPhpConfig() { - if ($this->localeStandard) { - return; + if (sprintf('%.1f', 1.0) !== '1.0') { + $this->systemLocale = setlocale(LC_NUMERIC, "0"); + setlocale(LC_NUMERIC, "C"); } - $this->systemLocale = setlocale(LC_NUMERIC, "0"); - setlocale(LC_NUMERIC, "C"); + $this->pcreJit = @ini_get('pcre.jit'); + @ini_set('pcre.jit', '0'); + + $this->mbstringEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); } /** * Restore the system's locale configuration */ - private function restoreLocale() + private function restorePhpConfig() { - if ($this->localeStandard) { - return; + if ($this->systemLocale !== null) { + setlocale(LC_NUMERIC, $this->systemLocale); + $this->systemLocale = null; } - setlocale(LC_NUMERIC, $this->systemLocale); + if ($this->pcreJit !== null) { + @ini_set('pcre.jit', $this->pcreJit); + $this->pcreJit = null; + } + + if ($this->mbstringEncoding !== null) { + mb_internal_encoding($this->mbstringEncoding); + $this->mbstringEncoding = null; + } } /** @@ -343,48 +331,43 @@ class Dompdf * Parse errors are stored in the global array _dompdf_warnings. * * @param string $file a filename or url to load + * @param string $encoding Encoding of $file * * @throws Exception */ - public function loadHtmlFile($file) + public function loadHtmlFile($file, $encoding = null) { - $this->saveLocale(); + $this->setPhpConfig(); if (!$this->protocol && !$this->baseHost && !$this->basePath) { - list($this->protocol, $this->baseHost, $this->basePath) = Helpers::explode_url($file); + [$this->protocol, $this->baseHost, $this->basePath] = Helpers::explode_url($file); } $protocol = strtolower($this->protocol); + $uri = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $file); - if ( !in_array($protocol, $this->allowedProtocols) ) { + $allowed_protocols = $this->options->getAllowedProtocols(); + if (!array_key_exists($protocol, $allowed_protocols)) { throw new Exception("Permission denied on $file. The communication protocol is not supported."); } - if (!$this->options->isRemoteEnabled() && ($protocol != "" && $protocol !== "file://")) { - throw new Exception("Remote file requested, but remote file download is disabled."); - } - - if ($protocol == "" || $protocol === "file://") { - $realfile = realpath($file); - - $chroot = realpath($this->options->getChroot()); - if ($chroot && strpos($realfile, $chroot) !== 0) { - throw new Exception("Permission denied on $file. The file could not be found under the directory specified by Options::chroot."); - } - - $ext = strtolower(pathinfo($realfile, PATHINFO_EXTENSION)); + if ($protocol === "file://") { + $ext = strtolower(pathinfo($uri, PATHINFO_EXTENSION)); if (!in_array($ext, $this->allowedLocalFileExtensions)) { - throw new Exception("Permission denied on $file. This file extension is forbidden"); + throw new Exception("Permission denied on $file: The file extension is forbidden."); } - - if (!$realfile) { - throw new Exception("File '$file' not found."); - } - - $file = $realfile; } - list($contents, $http_response_header) = Helpers::getFileContent($file, $this->httpContext); - $encoding = 'UTF-8'; + foreach ($allowed_protocols[$protocol]["rules"] as $rule) { + [$result, $message] = $rule($uri); + if (!$result) { + throw new Exception("Error loading $file: $message"); + } + } + + [$contents, $http_response_header] = Helpers::getFileContent($uri, $this->options->getHttpContext()); + if ($contents === null) { + throw new Exception("File '$file' not found."); + } // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/ if (isset($http_response_header)) { @@ -396,7 +379,7 @@ class Dompdf } } - $this->restoreLocale(); + $this->restorePhpConfig(); $this->loadHtml($contents, $encoding); } @@ -406,55 +389,75 @@ class Dompdf * @param string $encoding * @deprecated */ - public function load_html($str, $encoding = 'UTF-8') + public function load_html($str, $encoding = null) { $this->loadHtml($str, $encoding); } + public function loadDOM($doc, $quirksmode = false) { + // Remove #text children nodes in nodes that shouldn't have + $tag_names = ["html", "head", "table", "tbody", "thead", "tfoot", "tr"]; + foreach ($tag_names as $tag_name) { + $nodes = $doc->getElementsByTagName($tag_name); + + foreach ($nodes as $node) { + self::removeTextNodes($node); + } + } + + $this->dom = $doc; + $this->quirksmode = $quirksmode; + $this->tree = new FrameTree($this->dom); + } + /** * Loads an HTML string * Parse errors are stored in the global array _dompdf_warnings. - * @todo use the $encoding variable * * @param string $str HTML text to load - * @param string $encoding Not used yet + * @param string $encoding Encoding of $str */ - public function loadHtml($str, $encoding = 'UTF-8') + public function loadHtml($str, $encoding = null) { - $this->saveLocale(); + $this->setPhpConfig(); - // FIXME: Determine character encoding, switch to UTF8, update meta tag. Need better http/file stream encoding detection, currently relies on text or meta tag. - $known_encodings = mb_list_encodings(); - mb_detect_order('auto'); - if (($file_encoding = mb_detect_encoding($str, null, true)) === false) { - $file_encoding = "auto"; - } - if (in_array(strtoupper($file_encoding), array('UTF-8','UTF8')) === false) { - $str = mb_convert_encoding($str, 'UTF-8', $file_encoding); + // Determine character encoding when $encoding parameter not used + if ($encoding === null) { + mb_detect_order('auto'); + if (($encoding = mb_detect_encoding($str, null, true)) === false) { + + //"auto" is expanded to "ASCII,JIS,UTF-8,EUC-JP,SJIS" + $encoding = "auto"; + } } - $metatags = array( + if (in_array(strtoupper($encoding), array('UTF-8','UTF8')) === false) { + $str = mb_convert_encoding($str, 'UTF-8', $encoding); + + //Update encoding after converting + $encoding = 'UTF-8'; + } + + $metatags = [ '@]*charset\s*=\s*["\']?\s*([^"\' ]+)@i', - ); + ]; foreach ($metatags as $metatag) { if (preg_match($metatag, $str, $matches)) { - if (isset($matches[1]) && in_array($matches[1], $known_encodings)) { + if (isset($matches[1]) && in_array($matches[1], mb_list_encodings())) { $document_encoding = $matches[1]; break; } } } - if (isset($document_encoding) && in_array(strtoupper($document_encoding), array('UTF-8','UTF8')) === false) { + if (isset($document_encoding) && in_array(strtoupper($document_encoding), ['UTF-8','UTF8']) === false) { $str = preg_replace('/charset=([^\s"]+)/i', 'charset=UTF-8', $str); } elseif (isset($document_encoding) === false && strpos($str, '') !== false) { $str = str_replace('', '', $str); } elseif (isset($document_encoding) === false) { $str = '' . $str; } - //FIXME: since we're not using this just yet - $encoding = 'UTF-8'; // remove BOM mark from UTF-8, it's treated as document text by DOMDocument // FIXME: roll this into the encoding detection using UTF-8/16/32 BOM (http://us2.php.net/manual/en/function.mb-detect-encoding.php#91051)? @@ -463,75 +466,28 @@ class Dompdf } // Store parsing warnings as messages - set_error_handler(array("\\Dompdf\\Helpers", "record_warnings")); + set_error_handler([Helpers::class, 'record_warnings']); - // @todo Take the quirksmode into account - // http://hsivonen.iki.fi/doctype/ - // https://developer.mozilla.org/en/mozilla's_quirks_mode - $quirksmode = false; + try { + // @todo Take the quirksmode into account + // https://quirks.spec.whatwg.org/ + // http://hsivonen.iki.fi/doctype/ + $quirksmode = false; - if ($this->options->isHtml5ParserEnabled() && class_exists("HTML5_Tokenizer")) { - $tokenizer = new HTML5_Tokenizer($str); - $tokenizer->parse(); - $doc = $tokenizer->save(); + $html5 = new HTML5(['encoding' => $encoding, 'disable_html_ns' => true]); + $dom = $html5->loadHTML($str); - // Remove #text children nodes in nodes that shouldn't have - $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr"); - foreach ($tag_names as $tag_name) { - $nodes = $doc->getElementsByTagName($tag_name); - - foreach ($nodes as $node) { - self::removeTextNodes($node); - } - } - - $quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS); - } else { - // loadHTML assumes ISO-8859-1 unless otherwise specified on the HTML document header. - // http://devzone.zend.com/1538/php-dom-xml-extension-encoding-processing/ (see #4) - // http://stackoverflow.com/a/11310258/264628 + // extra step to normalize the HTML document structure + // see Masterminds/html5-php#166 $doc = new DOMDocument("1.0", $encoding); $doc->preserveWhiteSpace = true; - $doc->loadHTML($str); - $doc->encoding = $encoding; + $doc->loadHTML($html5->saveHTML($dom), LIBXML_NOWARNING | LIBXML_NOERROR); - // Remove #text children nodes in nodes that shouldn't have - $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr"); - foreach ($tag_names as $tag_name) { - $nodes = $doc->getElementsByTagName($tag_name); - - foreach ($nodes as $node) { - self::removeTextNodes($node); - } - } - - // If some text is before the doctype, we are in quirksmode - if (preg_match("/^(.+) - if (!$doc->doctype->publicId && !$doc->doctype->systemId) { - $quirksmode = false; - } - - // not XHTML - if (!preg_match("/xhtml/i", $doc->doctype->publicId)) { - $quirksmode = true; - } - } + $this->loadDOM($doc, $quirksmode); + } finally { + restore_error_handler(); + $this->restorePhpConfig(); } - - $this->dom = $doc; - $this->quirksmode = $quirksmode; - - $this->tree = new FrameTree($this->dom); - - restore_error_handler(); - - $this->restoreLocale(); } /** @@ -548,7 +504,7 @@ class Dompdf */ public static function removeTextNodes(DOMNode $node) { - $children = array(); + $children = []; for ($i = 0; $i < $node->childNodes->length; $i++) { $child = $node->childNodes->item($i); if ($child->nodeName === "#text") { @@ -569,15 +525,17 @@ class Dompdf { $this->tree->build_tree(); - $this->css->load_css_file(Stylesheet::getDefaultStylesheet(), Stylesheet::ORIG_UA); + $this->css->load_css_file($this->css->getDefaultStylesheet(), Stylesheet::ORIG_UA); $acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES; $acceptedmedia[] = $this->options->getDefaultMediaType(); // - $base_nodes = $this->dom->getElementsByTagName("base"); - if ($base_nodes->length && ($href = $base_nodes->item(0)->getAttribute("href"))) { - list($this->protocol, $this->baseHost, $this->basePath) = Helpers::explode_url($href); + /** @var \DOMElement|null */ + $baseNode = $this->dom->getElementsByTagName("base")->item(0); + $baseHref = $baseNode ? $baseNode->getAttribute("href") : ""; + if ($baseHref !== "") { + [$this->protocol, $this->baseHost, $this->basePath] = Helpers::explode_url($baseHref); } // Set the base path of the Stylesheet to that of the file being processed @@ -619,7 +577,9 @@ class Dompdf $url = $tag->getAttribute("href"); $url = Helpers::build_url($this->protocol, $this->baseHost, $this->basePath, $url); - $this->css->load_css_file($url, Stylesheet::ORIG_AUTHOR); + if ($url !== null) { + $this->css->load_css_file($url, Stylesheet::ORIG_AUTHOR); + } } break; @@ -647,9 +607,19 @@ class Dompdf $css = $tag->nodeValue; } + // Set the base path of the Stylesheet to that of the file being processed + $this->css->set_protocol($this->protocol); + $this->css->set_host($this->baseHost); + $this->css->set_base_path($this->basePath); + $this->css->load_css($css, Stylesheet::ORIG_AUTHOR); break; } + + // Set the base path of the Stylesheet to that of the file being processed + $this->css->set_protocol($this->protocol); + $this->css->set_host($this->baseHost); + $this->css->set_base_path($this->basePath); } } @@ -688,7 +658,7 @@ class Dompdf */ public function parseDefaultView($value) { - $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV"); + $valid = ["XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV"]; $options = preg_split("/\s*,\s*/", trim($value)); $defaultView = array_shift($options); @@ -706,16 +676,15 @@ class Dompdf */ public function render() { - $this->saveLocale(); - $options = $this->options; + $this->setPhpConfig(); - $logOutputFile = $options->getLogOutputFile(); + $logOutputFile = $this->options->getLogOutputFile(); if ($logOutputFile) { if (!file_exists($logOutputFile) && is_writable(dirname($logOutputFile))) { touch($logOutputFile); } - $this->startTime = microtime(true); + $startTime = microtime(true); if (is_writable($logOutputFile)) { ob_start(); } @@ -734,82 +703,31 @@ class Dompdf $pageStyle->inherit($basePageStyle); } - $defaultOptionPaperSize = $this->getPaperSize($options->getDefaultPaperSize()); - // If there is a CSS defined paper size compare to the paper size used to create the canvas to determine a - // recreation need + // Set paper size if defined via CSS if (is_array($basePageStyle->size)) { - $basePageStyleSize = $basePageStyle->size; - $this->setPaper(array(0, 0, $basePageStyleSize[0], $basePageStyleSize[1])); + [$width, $height] = $basePageStyle->size; + $this->setPaper([0, 0, $width, $height]); } - $paperSize = $this->getPaperSize(); - if ( - $defaultOptionPaperSize[2] !== $paperSize[2] || - $defaultOptionPaperSize[3] !== $paperSize[3] || - $options->getDefaultPaperOrientation() !== $this->paperOrientation - ) { - $this->setCanvas(CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation)); - $this->fontMetrics->setCanvas($this->getCanvas()); + // Create a new canvas instance if the current one does not match the + // desired paper size + $canvasWidth = $this->canvas->get_width(); + $canvasHeight = $this->canvas->get_height(); + $size = $this->getPaperSize(); + + if ($canvasWidth !== $size[2] || $canvasHeight !== $size[3]) { + $this->canvas = CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation); + $this->fontMetrics->setCanvas($this->canvas); } - $canvas = $this->getCanvas(); + $canvas = $this->canvas; - if ($options->isFontSubsettingEnabled() && $canvas instanceof CPDF) { - foreach ($this->tree->get_frames() as $frame) { - $style = $frame->get_style(); - $node = $frame->get_node(); - - // Handle text nodes - if ($node->nodeName === "#text") { - $chars = mb_strtoupper($node->nodeValue) . mb_strtolower($node->nodeValue); - $canvas->register_string_subset($style->font_family, $chars); - continue; - } - - // Handle generated content (list items) - if ($style->display === "list-item") { - $chars = ListBullet::get_counter_chars($style->list_style_type); - $canvas->register_string_subset($style->font_family, $chars); - $canvas->register_string_subset($style->font_family, '.'); - continue; - } - - // Handle other generated content (pseudo elements) - // FIXME: This only captures the text of the stylesheet declaration, - // not the actual generated content, and forces all possible counter - // values. See notes in issue #750. - if ($frame->get_node()->nodeName == "dompdf_generated") { - // all possible counter values, just in case - $chars = ListBullet::get_counter_chars('decimal'); - $canvas->register_string_subset($style->font_family, $chars); - $chars = ListBullet::get_counter_chars('upper-alpha'); - $canvas->register_string_subset($style->font_family, $chars); - $chars = ListBullet::get_counter_chars('lower-alpha'); - $canvas->register_string_subset($style->font_family, $chars); - $chars = ListBullet::get_counter_chars('lower-greek'); - $canvas->register_string_subset($style->font_family, $chars); - - // the hex-decoded text of the content property, duplicated from AbstrctFrameReflower::_parse_string - $decoded_string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", - function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); }, - $style->content); - $chars = mb_strtoupper($style->content) . mb_strtolower($style->content) . mb_strtoupper($decoded_string) . mb_strtolower($decoded_string); - $canvas->register_string_subset($style->font_family, $chars); - continue; - } - } - } - - $root = null; - - foreach ($this->tree->get_frames() as $frame) { - // Set up the root frame - if (is_null($root)) { - $root = Factory::decorate_root($this->tree->get_root(), $this); + $root_frame = $this->tree->get_root(); + $root = Factory::decorate_root($root_frame, $this); + foreach ($this->tree as $frame) { + if ($frame === $root_frame) { continue; } - - // Create the appropriate decorators, reflowers & positioners. Factory::decorate_frame($frame, $this, $root); } @@ -820,11 +738,11 @@ class Dompdf } $metas = $this->dom->getElementsByTagName("meta"); - $labels = array( + $labels = [ "author" => "Author", "keywords" => "Keywords", "description" => "Subject", - ); + ]; /** @var \DOMElement $meta */ foreach ($metas as $meta) { $name = mb_strtolower($meta->getAttribute("name")); @@ -840,14 +758,24 @@ class Dompdf } } - $root->set_containing_block(0, 0,$canvas->get_width(), $canvas->get_height()); + $root->set_containing_block(0, 0, $canvas->get_width(), $canvas->get_height()); $root->set_renderer(new Renderer($this)); // This is where the magic happens: $root->reflow(); + if (isset($this->callbacks["end_document"])) { + $fs = $this->callbacks["end_document"]; + + foreach ($fs as $f) { + $canvas->page_script($f); + } + } + // Clean up cached images - Cache::clear(); + if (!$this->options->getDebugKeepTemp()) { + Cache::clear($this->options->getDebugPng()); + } global $_dompdf_warnings, $_dompdf_show_warnings; if ($_dompdf_show_warnings && isset($_dompdf_warnings)) { @@ -864,39 +792,24 @@ class Dompdf } if ($logOutputFile && is_writable($logOutputFile)) { - $this->write_log(); + $this->writeLog($logOutputFile, $startTime); ob_end_clean(); } - $this->restoreLocale(); - } - - /** - * Add meta information to the PDF after rendering - */ - public function add_info($label, $value) - { - $canvas = $this->getCanvas(); - if (!is_null($canvas)) { - $canvas->add_info($label, $value); - } + $this->restorePhpConfig(); } /** * Writes the output buffer in the log file * - * @return void + * @param string $logOutputFile + * @param float $startTime */ - private function write_log() + private function writeLog(string $logOutputFile, float $startTime): void { - $log_output_file = $this->getOptions()->getLogOutputFile(); - if (!$log_output_file || !is_writable($log_output_file)) { - return; - } - $frames = Frame::$ID_COUNTER; $memory = memory_get_peak_usage(true) / 1024; - $time = (microtime(true) - $this->startTime) * 1000; + $time = (microtime(true) - $startTime) * 1000; $out = sprintf( "%6d" . @@ -909,7 +822,28 @@ class Dompdf $out .= ob_get_contents(); ob_clean(); - file_put_contents($log_output_file, $out); + file_put_contents($logOutputFile, $out); + } + + /** + * Add meta information to the PDF after rendering. + * + * @deprecated + */ + public function add_info($label, $value) + { + $this->addInfo($label, $value); + } + + /** + * Add meta information to the PDF after rendering. + * + * @param string $label Label of the value (Creator, Producer, etc.) + * @param string $value The text to set + */ + public function addInfo(string $label, string $value): void + { + $this->canvas->add_info($label, $value); } /** @@ -928,16 +862,13 @@ class Dompdf * @param string $filename the name of the streamed file * @param array $options header options (see above) */ - public function stream($filename = "document.pdf", $options = array()) + public function stream($filename = "document.pdf", $options = []) { - $this->saveLocale(); + $this->setPhpConfig(); - $canvas = $this->getCanvas(); - if (!is_null($canvas)) { - $canvas->stream($filename, $options); - } + $this->canvas->stream($filename, $options); - $this->restoreLocale(); + $this->restorePhpConfig(); } /** @@ -952,18 +883,13 @@ class Dompdf * * @return string|null */ - public function output($options = array()) + public function output($options = []) { - $this->saveLocale(); + $this->setPhpConfig(); - $canvas = $this->getCanvas(); - if (is_null($canvas)) { - return null; - } + $output = $this->canvas->output($options); - $output = $canvas->output($options); - - $this->restoreLocale(); + $this->restorePhpConfig(); return $output; } @@ -1035,7 +961,7 @@ class Dompdf /** * Sets the paper size & orientation * - * @param string|array $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES} + * @param string|float[] $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES} * @param string $orientation 'portrait' or 'landscape' * @return $this */ @@ -1049,19 +975,25 @@ class Dompdf /** * Gets the paper size * - * @param null|string|array $paperSize - * @return int[] A four-element integer array + * @return float[] A four-element float array */ - public function getPaperSize($paperSize = null) + public function getPaperSize() { - $size = $paperSize !== null ? $paperSize : $this->paperSize; - if (is_array($size)) { - return $size; - } else if (isset(Adapter\CPDF::$PAPER_SIZES[mb_strtolower($size)])) { - return Adapter\CPDF::$PAPER_SIZES[mb_strtolower($size)]; + $paper = $this->paperSize; + $orientation = $this->paperOrientation; + + if (is_array($paper)) { + $size = array_map("floatval", $paper); } else { - return Adapter\CPDF::$PAPER_SIZES["letter"]; + $paper = strtolower($paper); + $size = CPDF::$PAPER_SIZES[$paper] ?? CPDF::$PAPER_SIZES["letter"]; } + + if (strtolower($orientation) === "landscape") { + [$size[2], $size[3]] = [$size[3], $size[2]]; + } + + return $size; } /** @@ -1120,7 +1052,7 @@ class Dompdf * @param string $protocol * @return $this */ - public function setProtocol($protocol) + public function setProtocol(string $protocol) { $this->protocol = $protocol; return $this; @@ -1160,7 +1092,7 @@ class Dompdf * @param string $baseHost * @return $this */ - public function setBaseHost($baseHost) + public function setBaseHost(string $baseHost) { $this->baseHost = $baseHost; return $this; @@ -1202,7 +1134,7 @@ class Dompdf * @param string $basePath * @return $this */ - public function setBasePath($basePath) + public function setBasePath(string $basePath) { $this->basePath = $basePath; return $this; @@ -1265,12 +1197,12 @@ class Dompdf /** * Sets the HTTP context * - * @param resource $httpContext + * @param resource|array $httpContext * @return $this */ public function setHttpContext($httpContext) { - $this->httpContext = $httpContext; + $this->options->setHttpContext($httpContext); return $this; } @@ -1290,10 +1222,15 @@ class Dompdf */ public function getHttpContext() { - return $this->httpContext; + return $this->options->getHttpContext(); } /** + * Set a custom `Canvas` instance to render the document to. + * + * Be aware that the instance will be replaced on render if the document + * defines a paper size different from the canvas. + * * @param Canvas $canvas * @return $this */ @@ -1384,8 +1321,13 @@ class Dompdf */ public function setOptions(Options $options) { + // For backwards compatibility + if ($this->options && $this->options->getHttpContext() && !$options->getHttpContext()) { + $options->setHttpContext($this->options->getHttpContext()); + } + $this->options = $options; - $fontMetrics = $this->getFontMetrics(); + $fontMetrics = $this->fontMetrics; if (isset($fontMetrics)) { $fontMetrics->setOptions($options); } @@ -1421,38 +1363,51 @@ class Dompdf /** * @param array $callbacks the set of callbacks to set + * @return $this * @deprecated */ public function set_callbacks($callbacks) { - $this->setCallbacks($callbacks); + return $this->setCallbacks($callbacks); } /** - * Sets callbacks for events like rendering of pages and elements. - * The callbacks array contains arrays with 'event' set to 'begin_page', - * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or - * object plus method to be called. + * Define callbacks that allow modifying the document during render. * - * The function 'f' must take an array as argument, which contains info - * about the event. + * The callbacks array should contain arrays with `event` set to a callback + * event name and `f` set to a function or any other callable. * - * @param array $callbacks the set of callbacks to set + * The available callback events are: + * * `begin_page_reflow`: called before page reflow + * * `begin_frame`: called before a frame is rendered + * * `end_frame`: called after frame rendering is complete + * * `begin_page_render`: called before a page is rendered + * * `end_page_render`: called after page rendering is complete + * * `end_document`: called for every page after rendering is complete + * + * The function `f` receives three arguments `Frame $frame`, `Canvas $canvas`, + * and `FontMetrics $fontMetrics` for all events but `end_document`. For + * `end_document`, the function receives four arguments `int $pageNumber`, + * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics` instead. + * + * @param array $callbacks The set of callbacks to set. + * @return $this */ - public function setCallbacks($callbacks) + public function setCallbacks(array $callbacks): self { - if (is_array($callbacks)) { - $this->callbacks = array(); - foreach ($callbacks as $c) { - if (is_array($c) && isset($c['event']) && isset($c['f'])) { - $event = $c['event']; - $f = $c['f']; - if (is_callable($f) && is_string($event)) { - $this->callbacks[$event][] = $f; - } + $this->callbacks = []; + + foreach ($callbacks as $c) { + if (is_array($c) && isset($c["event"]) && isset($c["f"])) { + $event = $c["event"]; + $f = $c["f"]; + if (is_string($event) && is_callable($f)) { + $this->callbacks[$event][] = $f; } } } + + return $this; } /** @@ -1505,12 +1460,11 @@ class Dompdf */ function __get($prop) { - switch ($prop) - { - case 'version' : + switch ($prop) { + case 'version': return $this->version; default: - throw new Exception( 'Invalid property: ' . $prop ); + throw new Exception('Invalid property: ' . $prop); } } } diff --git a/library/vendor/dompdf/src/Exception.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception.php similarity index 84% rename from library/vendor/dompdf/src/Exception.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception.php index c9fb0df07..3a90e4771 100644 --- a/library/vendor/dompdf/src/Exception.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception.php @@ -1,11 +1,9 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf; /** diff --git a/library/vendor/dompdf/src/Exception/ImageException.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception/ImageException.php similarity index 84% rename from library/vendor/dompdf/src/Exception/ImageException.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception/ImageException.php index 62b44b1c8..ea1dfe407 100644 --- a/library/vendor/dompdf/src/Exception/ImageException.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Exception/ImageException.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Exception; diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FontMetrics.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FontMetrics.php new file mode 100644 index 000000000..5698c8823 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FontMetrics.php @@ -0,0 +1,635 @@ +setCanvas($canvas); + $this->setOptions($options); + $this->loadFontFamilies(); + } + + /** + * @deprecated + */ + public function save_font_families() + { + $this->saveFontFamilies(); + } + + /** + * Saves the stored font family cache + * + * The name and location of the cache file are determined by {@link + * FontMetrics::USER_FONTS_FILE}. This file should be writable by the + * webserver process. + * + * @see FontMetrics::loadFontFamilies() + */ + public function saveFontFamilies() + { + file_put_contents($this->getUserFontsFilePath(), json_encode($this->userFonts, JSON_PRETTY_PRINT)); + } + + /** + * @deprecated + */ + public function load_font_families() + { + $this->loadFontFamilies(); + } + + /** + * Loads the stored font family cache + * + * @see FontMetrics::saveFontFamilies() + */ + public function loadFontFamilies() + { + $file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json"; + $this->bundledFonts = json_decode(file_get_contents($file), true); + + if (is_readable($this->getUserFontsFilePath())) { + $this->userFonts = json_decode(file_get_contents($this->getUserFontsFilePath()), true); + } else { + $this->loadFontFamiliesLegacy(); + } + } + + private function loadFontFamiliesLegacy() + { + $legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php'; + if (is_readable($legacyCacheFile)) { + $fontDir = $this->options->getFontDir(); + $rootDir = $this->options->getRootDir(); + + if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); } + if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); } + + $cacheDataClosure = require $legacyCacheFile; + $cacheData = is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir); + if (is_array($cacheData)) { + foreach ($cacheData as $family => $variants) { + if (!isset($this->bundledFonts[$family]) && is_array($variants)) { + foreach ($variants as $variant => $variantPath) { + $variantName = basename($variantPath); + $variantDir = dirname($variantPath); + if ($variantDir == $fontDir) { + $this->userFonts[$family][$variant] = $variantName; + } else { + $this->userFonts[$family][$variant] = $variantPath; + } + } + } + } + $this->saveFontFamilies(); + } + } + } + + /** + * @param array $style + * @param string $remote_file + * @param resource $context + * @return bool + * @deprecated + */ + public function register_font($style, $remote_file, $context = null) + { + return $this->registerFont($style, $remote_file); + } + + /** + * @param array $style + * @param string $remoteFile + * @param resource $context + * @return bool + */ + public function registerFont($style, $remoteFile, $context = null) + { + $fontname = mb_strtolower($style["family"]); + $families = $this->getFontFamilies(); + + $entry = []; + if (isset($families[$fontname])) { + $entry = $families[$fontname]; + } + + $styleString = $this->getType("{$style['weight']} {$style['style']}"); + + $remoteHash = md5($remoteFile); + + $prefix = $fontname . "_" . $styleString; + $prefix = trim($prefix, "-"); + if (function_exists('iconv')) { + $prefix = @iconv('utf-8', 'us-ascii//TRANSLIT', $prefix); + } + $prefix_encoding = mb_detect_encoding($prefix, mb_detect_order(), true); + $substchar = mb_substitute_character(); + mb_substitute_character(0x005F); + $prefix = mb_convert_encoding($prefix, "ISO-8859-1", $prefix_encoding); + mb_substitute_character($substchar); + $prefix = preg_replace("[\W]", "_", $prefix); + $prefix = preg_replace("/[^-_\w]+/", "", $prefix); + + $localFile = $prefix . "_" . $remoteHash; + $localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile; + + if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) { + return true; + } + + + $entry[$styleString] = $localFile; + + // Download the remote file + [$protocol] = Helpers::explode_url($remoteFile); + $allowed_protocols = $this->options->getAllowedProtocols(); + if (!array_key_exists($protocol, $allowed_protocols)) { + Helpers::record_warnings(E_USER_WARNING, "Permission denied on $remoteFile. The communication protocol is not supported.", __FILE__, __LINE__); + return false; + } + + foreach ($allowed_protocols[$protocol]["rules"] as $rule) { + [$result, $message] = $rule($remoteFile); + if ($result !== true) { + Helpers::record_warnings(E_USER_WARNING, "Error loading $remoteFile: $message", __FILE__, __LINE__); + return false; + } + } + + list($remoteFileContent, $http_response_header) = @Helpers::getFileContent($remoteFile, $context); + if ($remoteFileContent === null) { + return false; + } + + $localTempFile = @tempnam($this->options->get("tempDir"), "dompdf-font-"); + file_put_contents($localTempFile, $remoteFileContent); + + $font = Font::load($localTempFile); + + if (!$font) { + unlink($localTempFile); + return false; + } + + $font->parse(); + $font->saveAdobeFontMetrics("$localFilePath.ufm"); + $font->close(); + + unlink($localTempFile); + + if ( !file_exists("$localFilePath.ufm") ) { + return false; + } + + $fontExtension = ".ttf"; + switch ($font->getFontType()) { + case "TrueType": + default: + $fontExtension = ".ttf"; + break; + } + + // Save the changes + file_put_contents($localFilePath.$fontExtension, $remoteFileContent); + + if ( !file_exists($localFilePath.$fontExtension) ) { + unlink("$localFilePath.ufm"); + return false; + } + + $this->setFontFamily($fontname, $entry); + + return true; + } + + /** + * @param $text + * @param $font + * @param $size + * @param float $word_spacing + * @param float $char_spacing + * @return float + * @deprecated + */ + public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) + { + //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); + return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + } + + /** + * Calculates text size, in points + * + * @param string $text The text to be sized + * @param string $font The font file to use + * @param float $size The font size, in points + * @param float $wordSpacing Word spacing, if any + * @param float $charSpacing Char spacing, if any + * + * @return float + */ + public function getTextWidth(string $text, $font, float $size, float $wordSpacing = 0.0, float $charSpacing = 0.0): float + { + // @todo Make sure this cache is efficient before enabling it + static $cache = []; + + if ($text === "") { + return 0; + } + + // Don't cache long strings + $useCache = !isset($text[50]); // Faster than strlen + + // Text-size calculations depend on the canvas used. Make sure to not + // return wrong values when switching canvas backends + $canvasClass = get_class($this->canvas); + $key = "$canvasClass/$font/$size/$wordSpacing/$charSpacing"; + + if ($useCache && isset($cache[$key][$text])) { + return $cache[$key][$text]; + } + + $width = $this->canvas->get_text_width($text, $font, $size, $wordSpacing, $charSpacing); + + if ($useCache) { + $cache[$key][$text] = $width; + } + + return $width; + } + + /** + * @param $font + * @param $size + * @return float + * @deprecated + */ + public function get_font_height($font, $size) + { + return $this->getFontHeight($font, $size); + } + + /** + * Calculates font height, in points + * + * @param string $font The font file to use + * @param float $size The font size, in points + * + * @return float + */ + public function getFontHeight($font, float $size): float + { + return $this->canvas->get_font_height($font, $size); + } + + /** + * Calculates font baseline, in points + * + * @param string $font The font file to use + * @param float $size The font size, in points + * + * @return float + */ + public function getFontBaseline($font, float $size): float + { + return $this->canvas->get_font_baseline($font, $size); + } + + /** + * @param $family_raw + * @param string $subtype_raw + * @return string + * @deprecated + */ + public function get_font($family_raw, $subtype_raw = "normal") + { + return $this->getFont($family_raw, $subtype_raw); + } + + /** + * Resolves a font family & subtype into an actual font file + * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If + * the particular font family has no suitable font file, the default font + * ({@link Options::defaultFont}) is used. The font file returned + * is the absolute pathname to the font file on the system. + * + * @param string|null $familyRaw + * @param string $subtypeRaw + * + * @return string|null + */ + public function getFont($familyRaw, $subtypeRaw = "normal") + { + static $cache = []; + + if (isset($cache[$familyRaw][$subtypeRaw])) { + return $cache[$familyRaw][$subtypeRaw]; + } + + /* Allow calling for various fonts in search path. Therefore not immediately + * return replacement on non match. + * Only when called with NULL try replacement. + * When this is also missing there is really trouble. + * If only the subtype fails, nevertheless return failure. + * Only on checking the fallback font, check various subtypes on same font. + */ + + $subtype = strtolower($subtypeRaw); + + $families = $this->getFontFamilies(); + if ($familyRaw) { + $family = str_replace(["'", '"'], "", strtolower($familyRaw)); + + if (isset($families[$family][$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype]; + } + + return null; + } + + $fallback_families = [strtolower($this->options->getDefaultFont()), "serif"]; + foreach ($fallback_families as $family) { + if (isset($families[$family][$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype]; + } + + if (!isset($families[$family])) { + continue; + } + + $family = $families[$family]; + + foreach ($family as $sub => $font) { + if (strpos($subtype, $sub) !== false) { + return $cache[$familyRaw][$subtypeRaw] = $font; + } + } + + if ($subtype !== "normal") { + foreach ($family as $sub => $font) { + if ($sub !== "normal") { + return $cache[$familyRaw][$subtypeRaw] = $font; + } + } + } + + $subtype = "normal"; + + if (isset($family[$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $family[$subtype]; + } + } + + return null; + } + + /** + * @param $family + * @return null|string + * @deprecated + */ + public function get_family($family) + { + return $this->getFamily($family); + } + + /** + * @param string $family + * @return null|string + */ + public function getFamily($family) + { + $family = str_replace(["'", '"'], "", mb_strtolower($family)); + $families = $this->getFontFamilies(); + + if (isset($families[$family])) { + return $families[$family]; + } + + return null; + } + + /** + * @param $type + * @return string + * @deprecated + */ + public function get_type($type) + { + return $this->getType($type); + } + + /** + * @param string $type + * @return string + */ + public function getType($type) + { + if (preg_match('/bold/i', $type)) { + $weight = 700; + } elseif (preg_match('/([1-9]00)/', $type, $match)) { + $weight = (int)$match[0]; + } else { + $weight = 400; + } + $weight = $weight === 400 ? 'normal' : $weight; + $weight = $weight === 700 ? 'bold' : $weight; + + $style = preg_match('/italic|oblique/i', $type) ? 'italic' : null; + + if ($weight === 'normal' && $style !== null) { + return $style; + } + + return $style === null + ? $weight + : $weight.'_'.$style; + } + + /** + * @return array + * @deprecated + */ + public function get_font_families() + { + return $this->getFontFamilies(); + } + + /** + * Returns the current font lookup table + * + * @return array + */ + public function getFontFamilies() + { + if (!isset($this->fontFamilies)) { + $this->setFontFamilies(); + } + return $this->fontFamilies; + } + + /** + * Convert loaded fonts to font lookup table + * + * @return array + */ + public function setFontFamilies() + { + $fontFamilies = []; + if (isset($this->bundledFonts) && is_array($this->bundledFonts)) { + foreach ($this->bundledFonts as $family => $variants) { + if (!isset($fontFamilies[$family])) { + $fontFamilies[$family] = array_map(function ($variant) { + return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant; + }, $variants); + } + } + } + if (isset($this->userFonts) && is_array($this->userFonts)) { + foreach ($this->userFonts as $family => $variants) { + $fontFamilies[$family] = array_map(function ($variant) { + $variantName = basename($variant); + if ($variantName === $variant) { + return $this->getOptions()->getFontDir() . '/' . $variant; + } + return $variant; + }, $variants); + } + } + $this->fontFamilies = $fontFamilies; + } + + /** + * @param string $fontname + * @param mixed $entry + * @deprecated + */ + public function set_font_family($fontname, $entry) + { + $this->setFontFamily($fontname, $entry); + } + + /** + * @param string $fontname + * @param mixed $entry + */ + public function setFontFamily($fontname, $entry) + { + $this->userFonts[mb_strtolower($fontname)] = $entry; + $this->saveFontFamilies(); + unset($this->fontFamilies); + } + + /** + * @return string + */ + public function getUserFontsFilePath() + { + return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE; + } + + /** + * @param Options $options + * @return $this + */ + public function setOptions(Options $options) + { + $this->options = $options; + unset($this->fontFamilies); + return $this; + } + + /** + * @return Options + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param Canvas $canvas + * @return $this + */ + public function setCanvas(Canvas $canvas) + { + $this->canvas = $canvas; + return $this; + } + + /** + * @return Canvas + */ + public function getCanvas() + { + return $this->canvas; + } +} diff --git a/library/vendor/dompdf/src/Frame.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame.php similarity index 76% rename from library/vendor/dompdf/src/Frame.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame.php index 31ea00a1f..55136b22b 100644 --- a/library/vendor/dompdf/src/Frame.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame.php @@ -1,16 +1,13 @@ - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ +use Dompdf\Frame\FrameListIterator; /** * The main Frame class @@ -39,12 +36,14 @@ class Frame * Unique identifier for this frame. Used to reference this frame * via the node. * - * @var string + * @var int */ protected $_id; /** * Unique id counter + * + * @var int */ public static $ID_COUNTER = 0; /*protected*/ @@ -55,14 +54,6 @@ class Frame */ protected $_style; - /** - * This frame's original style. Needed for cases where frames are - * split across pages. - * - * @var Style - */ - protected $_original_style; - /** * This frame's parent in the document tree. * @@ -70,13 +61,6 @@ class Frame */ protected $_parent; - /** - * This frame's children - * - * @var Frame[] - */ - protected $_frame_list; - /** * This frame's first child. All children are handled as a * doubly-linked list. @@ -131,21 +115,21 @@ class Frame /** * This frame's decorator * - * @var \Dompdf\FrameDecorator\AbstractFrameDecorator + * @var FrameDecorator\AbstractFrameDecorator */ protected $_decorator; /** * This frame's containing line box * - * @var LineBox + * @var LineBox|null */ protected $_containing_line; /** * @var array */ - protected $_is_cache = array(); + protected $_is_cache = []; /** * Tells whether the frame was already pushed to the next page @@ -159,13 +143,6 @@ class Frame */ public $_float_next_line = false; - /** - * Tells whether the frame was split - * - * @var bool - */ - public $_splitted; - /** * @var int */ @@ -186,24 +163,23 @@ class Frame $this->_prev_sibling = $this->_next_sibling = null; $this->_style = null; - $this->_original_style = null; - $this->_containing_block = array( + $this->_containing_block = [ "x" => null, "y" => null, "w" => null, "h" => null, - ); + ]; $this->_containing_block[0] =& $this->_containing_block["x"]; $this->_containing_block[1] =& $this->_containing_block["y"]; $this->_containing_block[2] =& $this->_containing_block["w"]; $this->_containing_block[3] =& $this->_containing_block["h"]; - $this->_position = array( + $this->_position = [ "x" => null, "y" => null, - ); + ]; $this->_position[0] =& $this->_position["x"]; $this->_position[1] =& $this->_position["y"]; @@ -240,7 +216,7 @@ class Frame { $whitespace = $this->get_style()->white_space; - return in_array($whitespace, array("pre", "pre-wrap", "pre-line")); + return in_array($whitespace, ["pre", "pre-wrap", "pre-line"]); } /** @@ -299,14 +275,8 @@ class Frame $this->_parent->get_node()->removeChild($this->_node); } - $this->_style->dispose(); $this->_style = null; unset($this->_style); - - $this->_original_style->dispose(); - $this->_original_style = null; - unset($this->_original_style); - } /** @@ -322,17 +292,7 @@ class Frame $this->_containing_block["w"] = null; $this->_containing_block["h"] = null; - $this->_style = null; - unset($this->_style); - $this->_style = clone $this->_original_style; - - // If this represents a generated node then child nodes represent generated content. - // Remove the children since the content will be generated next time this frame is reflowed. - if ($this->_node->nodeName === "dompdf_generated" && $this->_style->content != "normal") { - foreach ($this->get_children() as $child) { - $this->remove_child($child); - } - } + $this->_style->reset(); } /** @@ -344,7 +304,7 @@ class Frame } /** - * @return string + * @return int */ public function get_id() { @@ -360,11 +320,12 @@ class Frame } /** + * @deprecated * @return Style */ public function get_original_style() { - return $this->_original_style; + return $this->_style; } /** @@ -376,7 +337,7 @@ class Frame } /** - * @return \Dompdf\FrameDecorator\AbstractFrameDecorator + * @return FrameDecorator\AbstractFrameDecorator */ public function get_decorator() { @@ -416,17 +377,11 @@ class Frame } /** - * @return FrameList|Frame[] + * @return FrameListIterator */ - public function get_children() + public function get_children(): FrameListIterator { - if (isset($this->_frame_list)) { - return $this->_frame_list; - } - - $this->_frame_list = new FrameList($this); - - return $this->_frame_list; + return new FrameListIterator($this); } // Layout property accessors @@ -434,7 +389,7 @@ class Frame /** * Containing block dimensions * - * @param $i string The key of the wanted containing block's dimension (x, y, w, h) + * @param string|null $i The key of the wanted containing block's dimension (x, y, w, h) * * @return float[]|float */ @@ -450,9 +405,9 @@ class Frame /** * Block position * - * @param $i string The key of the wanted position value (x, y) + * @param string|null $i The key of the wanted position value (x, y) * - * @return array|float + * @return float[]|float */ public function get_position($i = null) { @@ -465,38 +420,17 @@ class Frame //........................................................................ - /** - * Return the height of the margin box of the frame, in pt. Meaningless - * unless the height has been calculated properly. - * - * @return float - */ - public function get_margin_height() - { - $style = $this->_style; - - return (float)$style->length_in_pt(array( - $style->height, - $style->margin_top, - $style->margin_bottom, - $style->border_top_width, - $style->border_bottom_width, - $style->padding_top, - $style->padding_bottom - ), $this->_containing_block["h"]); - } - /** * Return the width of the margin box of the frame, in pt. Meaningless * unless the width has been calculated properly. * * @return float */ - public function get_margin_width() + public function get_margin_width(): float { $style = $this->_style; - return (float)$style->length_in_pt(array( + return (float)$style->length_in_pt([ $style->width, $style->margin_left, $style->margin_right, @@ -504,135 +438,185 @@ class Frame $style->border_right_width, $style->padding_left, $style->padding_right - ), $this->_containing_block["w"]); + ], $this->_containing_block["w"]); } /** + * Return the height of the margin box of the frame, in pt. Meaningless + * unless the height has been calculated properly. + * * @return float */ - public function get_break_margins() + public function get_margin_height(): float { $style = $this->_style; - return (float)$style->length_in_pt(array( - //$style->height, - $style->margin_top, - $style->margin_bottom, - $style->border_top_width, - $style->border_bottom_width, - $style->padding_top, - $style->padding_bottom - ), $this->_containing_block["h"]); - } - - /** - * Return the content box (x,y,w,h) of the frame - * - * @return array - */ - public function get_content_box() - { - $style = $this->_style; - $cb = $this->_containing_block; - - $x = $this->_position["x"] + - (float)$style->length_in_pt(array($style->margin_left, - $style->border_left_width, - $style->padding_left), - $cb["w"]); - - $y = $this->_position["y"] + - (float)$style->length_in_pt(array($style->margin_top, - $style->border_top_width, - $style->padding_top), - $cb["h"]); - - $w = $style->length_in_pt($style->width, $cb["w"]); - - $h = $style->length_in_pt($style->height, $cb["h"]); - - return array(0 => $x, "x" => $x, - 1 => $y, "y" => $y, - 2 => $w, "w" => $w, - 3 => $h, "h" => $h); - } - - /** - * Return the padding box (x,y,w,h) of the frame - * - * @return array - */ - public function get_padding_box() - { - $style = $this->_style; - $cb = $this->_containing_block; - - $x = $this->_position["x"] + - (float)$style->length_in_pt(array($style->margin_left, - $style->border_left_width), - $cb["w"]); - - $y = $this->_position["y"] + - (float)$style->length_in_pt(array($style->margin_top, - $style->border_top_width), - $cb["h"]); - - $w = $style->length_in_pt(array($style->padding_left, - $style->width, - $style->padding_right), - $cb["w"]); - - $h = $style->length_in_pt(array($style->padding_top, + return (float)$style->length_in_pt( + [ $style->height, - $style->padding_bottom), - $cb["h"]); - - return array(0 => $x, "x" => $x, - 1 => $y, "y" => $y, - 2 => $w, "w" => $w, - 3 => $h, "h" => $h); + (float)$style->length_in_pt( + [ + $style->border_top_width, + $style->border_bottom_width, + $style->margin_top, + $style->margin_bottom, + $style->padding_top, + $style->padding_bottom + ], $this->_containing_block["w"] + ) + ], + $this->_containing_block["h"] + ); } /** - * Return the border box of the frame + * Return the content box (x,y,w,h) of the frame. * - * @return array + * Width and height might be reported as 0 if they have not been resolved + * yet. + * + * @return float[] */ - public function get_border_box() + public function get_content_box(): array + { + $style = $this->_style; + $cb = $this->_containing_block; + + $x = $this->_position["x"] + + (float)$style->length_in_pt( + [ + $style->margin_left, + $style->border_left_width, + $style->padding_left + ], + $cb["w"] + ); + + $y = $this->_position["y"] + + (float)$style->length_in_pt( + [ + $style->margin_top, + $style->border_top_width, + $style->padding_top + ], $cb["w"] + ); + + $w = (float)$style->length_in_pt($style->width, $cb["w"]); + + $h = (float)$style->length_in_pt($style->height, $cb["h"]); + + return [0 => $x, "x" => $x, + 1 => $y, "y" => $y, + 2 => $w, "w" => $w, + 3 => $h, "h" => $h]; + } + + /** + * Return the padding box (x,y,w,h) of the frame. + * + * Width and height might be reported as 0 if they have not been resolved + * yet. + * + * @return float[] + */ + public function get_padding_box(): array + { + $style = $this->_style; + $cb = $this->_containing_block; + + $x = $this->_position["x"] + + (float)$style->length_in_pt( + [ + $style->margin_left, + $style->border_left_width + ], + $cb["w"] + ); + + $y = $this->_position["y"] + + (float)$style->length_in_pt( + [ + $style->margin_top, + $style->border_top_width + ], + $cb["h"] + ); + + $w = (float)$style->length_in_pt( + [ + $style->padding_left, + $style->width, + $style->padding_right + ], + $cb["w"] + ); + + $h = (float)$style->length_in_pt( + [ + $style->padding_top, + $style->padding_bottom, + $style->length_in_pt($style->height, $cb["h"]) + ], + $cb["w"] + ); + + return [0 => $x, "x" => $x, + 1 => $y, "y" => $y, + 2 => $w, "w" => $w, + 3 => $h, "h" => $h]; + } + + /** + * Return the border box of the frame. + * + * Width and height might be reported as 0 if they have not been resolved + * yet. + * + * @return float[] + */ + public function get_border_box(): array { $style = $this->_style; $cb = $this->_containing_block; $x = $this->_position["x"] + (float)$style->length_in_pt($style->margin_left, $cb["w"]); - $y = $this->_position["y"] + (float)$style->length_in_pt($style->margin_top, $cb["h"]); + $y = $this->_position["y"] + (float)$style->length_in_pt($style->margin_top, $cb["w"]); - $w = $style->length_in_pt(array($style->border_left_width, + $w = (float)$style->length_in_pt( + [ + $style->border_left_width, $style->padding_left, $style->width, $style->padding_right, - $style->border_right_width), - $cb["w"]); + $style->border_right_width + ], + $cb["w"] + ); - $h = $style->length_in_pt(array($style->border_top_width, + $h = (float)$style->length_in_pt( + [ + $style->border_top_width, $style->padding_top, - $style->height, $style->padding_bottom, - $style->border_bottom_width), - $cb["h"]); + $style->border_bottom_width, + $style->length_in_pt($style->height, $cb["h"]) + ], + $cb["w"] + ); - return array(0 => $x, "x" => $x, + return [0 => $x, "x" => $x, 1 => $y, "y" => $y, 2 => $w, "w" => $w, - 3 => $h, "h" => $h); + 3 => $h, "h" => $h]; } /** - * @param null $opacity + * @param float|null $opacity * * @return float */ - public function get_opacity($opacity = null) + public function get_opacity(?float $opacity = null): float { if ($opacity !== null) { $this->set_opacity($opacity); @@ -642,7 +626,7 @@ class Frame } /** - * @return LineBox + * @return LineBox|null */ public function &get_containing_line() { @@ -650,10 +634,10 @@ class Frame } //........................................................................ - // Set methods + /** - * @param $id + * @param int $id */ public function set_id($id) { @@ -670,18 +654,14 @@ class Frame /** * @param Style $style */ - public function set_style(Style $style) + public function set_style(Style $style): void { - if (is_null($this->_style)) { - $this->_original_style = clone $style; - } - - //$style->set_frame($this); + // $style->set_frame($this); $this->_style = $style; } /** - * @param \Dompdf\FrameDecorator\AbstractFrameDecorator $decorator + * @param FrameDecorator\AbstractFrameDecorator $decorator */ public function set_decorator(FrameDecorator\AbstractFrameDecorator $decorator) { @@ -689,10 +669,10 @@ class Frame } /** - * @param null $x - * @param null $y - * @param null $w - * @param null $h + * @param float|float[]|null $x + * @param float|null $y + * @param float|null $w + * @param float|null $h */ public function set_containing_block($x = null, $y = null, $w = null, $h = null) { @@ -720,13 +700,13 @@ class Frame } /** - * @param null $x - * @param null $y + * @param float|float[]|null $x + * @param float|null $y */ public function set_position($x = null, $y = null) { if (is_array($x)) { - list($x, $y) = array($x["x"], $x["y"]); + list($x, $y) = [$x["x"], $x["y"]]; } if (is_numeric($x)) { @@ -739,12 +719,12 @@ class Frame } /** - * @param $opacity + * @param float $opacity */ - public function set_opacity($opacity) + public function set_opacity(float $opacity): void { $parent = $this->get_parent(); - $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0); + $base_opacity = $parent && $parent->_opacity !== null ? $parent->_opacity : 1.0; $this->_opacity = $base_opacity * $opacity; } @@ -767,7 +747,7 @@ class Frame return in_array( "auto", - array( + [ $style->height, $style->margin_top, $style->margin_bottom, @@ -776,7 +756,7 @@ class Frame $style->padding_top, $style->padding_bottom, $this->_containing_block["h"] - ), + ], true ); } @@ -792,7 +772,7 @@ class Frame return in_array( "auto", - array( + [ $style->width, $style->margin_left, $style->margin_right, @@ -801,7 +781,7 @@ class Frame $style->padding_left, $style->padding_right, $this->_containing_block["w"] - ), + ], true ); } @@ -811,7 +791,7 @@ class Frame * * @return bool */ - public function is_text_node() + public function is_text_node(): bool { if (isset($this->_is_cache["text_node"])) { return $this->_is_cache["text_node"]; @@ -823,70 +803,91 @@ class Frame /** * @return bool */ - public function is_positionned() + public function is_positioned(): bool { - if (isset($this->_is_cache["positionned"])) { - return $this->_is_cache["positionned"]; + if (isset($this->_is_cache["positioned"])) { + return $this->_is_cache["positioned"]; } $position = $this->get_style()->position; - return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES); + return $this->_is_cache["positioned"] = in_array($position, Style::POSITIONED_TYPES, true); } /** * @return bool */ - public function is_absolute() + public function is_absolute(): bool { if (isset($this->_is_cache["absolute"])) { return $this->_is_cache["absolute"]; } - $position = $this->get_style()->position; - - return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed"); + return $this->_is_cache["absolute"] = $this->get_style()->is_absolute(); } /** + * Whether the frame is a block container. + * * @return bool */ - public function is_block() + public function is_block(): bool { if (isset($this->_is_cache["block"])) { return $this->_is_cache["block"]; } - return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES); + return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::BLOCK_TYPES, true); } /** + * Whether the frame has a block-level display type. + * * @return bool */ - public function is_inline_block() + public function is_block_level(): bool { - if (isset($this->_is_cache["inline_block"])) { - return $this->_is_cache["inline_block"]; + if (isset($this->_is_cache["block_level"])) { + return $this->_is_cache["block_level"]; } - return $this->_is_cache["inline_block"] = ($this->get_style()->display === 'inline-block'); + $display = $this->get_style()->display; + + return $this->_is_cache["block_level"] = in_array($display, Style::BLOCK_LEVEL_TYPES, true); + } + + /** + * Whether the frame has an inline-level display type. + * + * @return bool + */ + public function is_inline_level(): bool + { + if (isset($this->_is_cache["inline_level"])) { + return $this->_is_cache["inline_level"]; + } + + $display = $this->get_style()->display; + + return $this->_is_cache["inline_level"] = in_array($display, Style::INLINE_LEVEL_TYPES, true); } /** * @return bool */ - public function is_in_flow() + public function is_in_flow(): bool { if (isset($this->_is_cache["in_flow"])) { return $this->_is_cache["in_flow"]; } - return $this->_is_cache["in_flow"] = !($this->get_style()->float !== "none" || $this->is_absolute()); + + return $this->_is_cache["in_flow"] = $this->get_style()->is_in_flow(); } /** * @return bool */ - public function is_pre() + public function is_pre(): bool { if (isset($this->_is_cache["pre"])) { return $this->_is_cache["pre"]; @@ -894,13 +895,13 @@ class Frame $white_space = $this->get_style()->white_space; - return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap")); + return $this->_is_cache["pre"] = in_array($white_space, ["pre", "pre-wrap"], true); } /** * @return bool */ - public function is_table() + public function is_table(): bool { if (isset($this->_is_cache["table"])) { return $this->_is_cache["table"]; @@ -908,15 +909,15 @@ class Frame $display = $this->get_style()->display; - return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES); + return $this->_is_cache["table"] = in_array($display, Style::TABLE_TYPES, true); } /** * Inserts a new child at the beginning of the Frame * - * @param $child Frame The new Frame to insert - * @param $update_node boolean Whether or not to update the DOM + * @param Frame $child The new Frame to insert + * @param bool $update_node Whether or not to update the DOM */ public function prepend_child(Frame $child, $update_node = true) { @@ -947,8 +948,8 @@ class Frame /** * Inserts a new child at the end of the Frame * - * @param $child Frame The new Frame to insert - * @param $update_node boolean Whether or not to update the DOM + * @param Frame $child The new Frame to insert + * @param bool $update_node Whether or not to update the DOM */ public function append_child(Frame $child, $update_node = true) { @@ -984,9 +985,9 @@ class Frame /** * Inserts a new child immediately before the specified frame * - * @param $new_child Frame The new Frame to insert - * @param $ref Frame The Frame after the new Frame - * @param $update_node boolean Whether or not to update the DOM + * @param Frame $new_child The new Frame to insert + * @param Frame $ref The Frame after the new Frame + * @param bool $update_node Whether or not to update the DOM * * @throws Exception */ @@ -1032,9 +1033,9 @@ class Frame /** * Inserts a new child immediately after the specified frame * - * @param $new_child Frame The new Frame to insert - * @param $ref Frame The Frame before the new Frame - * @param $update_node boolean Whether or not to update the DOM + * @param Frame $new_child The new Frame to insert + * @param Frame $ref The Frame before the new Frame + * @param bool $update_node Whether or not to update the DOM * * @throws Exception */ @@ -1086,7 +1087,7 @@ class Frame * Remove a child frame * * @param Frame $child - * @param boolean $update_node Whether or not to remove the DOM node + * @param bool $update_node Whether or not to remove the DOM node * * @throws Exception * @return Frame The removed child frame @@ -1206,11 +1207,11 @@ class Frame $str .= "\n"; if (php_sapi_name() === "cli") { - $str = strip_tags(str_replace(array("
", "", ""), - array("\n", "", ""), + $str = strip_tags(str_replace(["
", "", ""], + ["\n", "", ""], $str)); } return $str; } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/src/Frame/Factory.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/Factory.php similarity index 85% rename from library/vendor/dompdf/src/Frame/Factory.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/Factory.php index e14f75b62..b4bab8871 100644 --- a/library/vendor/dompdf/src/Frame/Factory.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/Factory.php @@ -1,13 +1,11 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Frame; -use Dompdf\Css\Style; use Dompdf\Dompdf; use Dompdf\Exception; use Dompdf\Frame; @@ -25,7 +23,6 @@ use Dompdf\Positioner\AbstractPositioner; * objects. This is determined primarily by the Frame's display type, but * also by the Frame's node's type (e.g. DomElement vs. #text) * - * @access private * @package dompdf */ class Factory @@ -60,7 +57,7 @@ class Factory * * @param Frame $frame The frame to decorate * @param Dompdf $dompdf The dompdf instance - * @param Frame $root The frame to decorate + * @param Frame $root The root of the frame * * @throws Exception * @return AbstractFrameDecorator @@ -68,31 +65,17 @@ class Factory */ static function decorate_frame(Frame $frame, Dompdf $dompdf, Frame $root = null) { - if (is_null($dompdf)) { - throw new Exception("The DOMPDF argument is required"); - } - $style = $frame->get_style(); - - // Floating (and more generally out-of-flow) elements are blocks - // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/ - if (!$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) { - $style->display = "block"; - } - $display = $style->display; switch ($display) { - case "flex": //FIXME: display type not yet supported - case "table-caption": //FIXME: display type not yet supported case "block": $positioner = "Block"; $decorator = "Block"; $reflower = "Block"; break; - case "inline-flex": //FIXME: display type not yet supported case "inline-block": $positioner = "Inline"; $decorator = "Block"; @@ -105,13 +88,8 @@ class Factory $decorator = "Text"; $reflower = "Text"; } else { - if ($style->float !== "none") { - $decorator = "Block"; - $reflower = "Block"; - } else { - $decorator = "Inline"; - $reflower = "Inline"; - } + $decorator = "Inline"; + $reflower = "Inline"; } break; @@ -182,7 +160,6 @@ class Factory break; default: - // FIXME: should throw some sort of warning or something? case "none": if ($style->_dompdf_keep !== "yes") { // Remove the node and the frame @@ -211,7 +188,7 @@ class Factory // Handle nodeName if ($node->nodeName === "img") { - $style->display = "-dompdf-image"; + $style->set_prop("display", "-dompdf-image"); $decorator = "Image"; $reflower = "Image"; } @@ -237,8 +214,7 @@ class Factory $node = $frame->get_node(); $parent_node = $node->parentNode; - - if ($parent_node) { + if ($parent_node && $parent_node instanceof \DOMElement) { if (!$parent_node->hasAttribute("dompdf-children-count")) { $xpath = new DOMXPath($xml); $count = $xpath->query("li", $parent_node)->length; @@ -260,7 +236,7 @@ class Factory } $new_style = $dompdf->getCss()->create_style(); - $new_style->display = "-dompdf-list-bullet"; + $new_style->set_prop("display", "-dompdf-list-bullet"); $new_style->inherit($style); $b_f->set_style($new_style); diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameListIterator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameListIterator.php new file mode 100644 index 000000000..01575505a --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameListIterator.php @@ -0,0 +1,100 @@ +parent = $frame; + $this->rewind(); + } + + public function rewind(): void + { + $this->cur = $this->parent->get_first_child(); + $this->prev = null; + $this->num = 0; + } + + /** + * @return bool + */ + public function valid(): bool + { + return $this->cur !== null; + } + + /** + * @return int + */ + public function key(): int + { + return $this->num; + } + + /** + * @return Frame|null + */ + public function current(): ?Frame + { + return $this->cur; + } + + public function next(): void + { + if ($this->cur === null) { + return; + } + + if ($this->cur->get_parent() === $this->parent) { + $this->prev = $this->cur; + $this->cur = $this->cur->get_next_sibling(); + $this->num++; + } else { + // Continue from the previous child if the current frame has been + // moved to another parent + $this->cur = $this->prev !== null + ? $this->prev->get_next_sibling() + : $this->parent->get_first_child(); + } + } +} diff --git a/library/vendor/dompdf/src/Frame/FrameTree.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTree.php similarity index 92% rename from library/vendor/dompdf/src/Frame/FrameTree.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTree.php index 1d2585439..6d012d8d3 100644 --- a/library/vendor/dompdf/src/Frame/FrameTree.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTree.php @@ -1,5 +1,9 @@ - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ +use IteratorAggregate; /** * Represents an entire document as a tree of frames @@ -27,14 +25,14 @@ use Dompdf\Frame; * * @package dompdf */ -class FrameTree +class FrameTree implements IteratorAggregate { /** * Tags to ignore while parsing the tree * * @var array */ - protected static $HIDDEN_TAGS = array( + protected static $HIDDEN_TAGS = [ "area", "base", "basefont", @@ -46,7 +44,7 @@ class FrameTree "noembed", "param", "#comment" - ); + ]; /** * The main DomDocument @@ -86,7 +84,7 @@ class FrameTree { $this->_dom = $dom; $this->_root = null; - $this->_registry = array(); + $this->_registry = []; } /** @@ -124,11 +122,22 @@ class FrameTree /** * Returns a post-order iterator for all frames in the tree * - * @return FrameTreeList|Frame[] + * @deprecated Iterate the tree directly instead + * @return FrameTreeIterator */ - public function get_frames() + public function get_frames(): FrameTreeIterator { - return new FrameTreeList($this->_root); + return new FrameTreeIterator($this->_root); + } + + /** + * Returns a post-order iterator for all frames in the tree + * + * @return FrameTreeIterator + */ + public function getIterator(): FrameTreeIterator + { + return new FrameTreeIterator($this->_root); } /** @@ -239,7 +248,7 @@ class FrameTree } // Store the children in an array so that the tree can be modified - $children = array(); + $children = []; $length = $node->childNodes->length; for ($i = 0; $i < $length; $i++) { $children[] = $node->childNodes->item($i); @@ -312,4 +321,4 @@ class FrameTree return $frame_id; } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/src/Frame/FrameTreeIterator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php similarity index 70% rename from library/vendor/dompdf/src/Frame/FrameTreeIterator.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php index ebca7e99f..4da8da1ee 100644 --- a/library/vendor/dompdf/src/Frame/FrameTreeIterator.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Frame/FrameTreeIterator.php @@ -1,4 +1,9 @@ _num = 0; } - /** - * - */ - public function rewind() + public function rewind(): void { - $this->_stack = array($this->_root); + $this->_stack = [$this->_root]; $this->_num = 0; } /** * @return bool */ - public function valid() + public function valid(): bool { return count($this->_stack) > 0; } @@ -58,7 +59,7 @@ class FrameTreeIterator implements Iterator /** * @return int */ - public function key() + public function key(): int { return $this->_num; } @@ -66,20 +67,14 @@ class FrameTreeIterator implements Iterator /** * @return Frame */ - public function current() + public function current(): Frame { return end($this->_stack); } - /** - * @return Frame - */ - public function next() + public function next(): void { - $b = end($this->_stack); - - // Pop last element - unset($this->_stack[key($this->_stack)]); + $b = array_pop($this->_stack); $this->_num++; // Push all children onto the stack in reverse order @@ -89,8 +84,5 @@ class FrameTreeIterator implements Iterator $this->_stack[] = $c; } } - - return $b; } } - diff --git a/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php similarity index 65% rename from library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php index 777fc3ce8..2c1fcc5df 100644 --- a/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php @@ -1,26 +1,23 @@ - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ /** * Base AbstractFrameDecorator class @@ -31,7 +28,12 @@ abstract class AbstractFrameDecorator extends Frame { const DEFAULT_COUNTER = "-dompdf-default-counter"; - public $_counters = array(); // array([id] => counter_value) (for generated content) + /** + * array([id] => counter_value) (for generated content) + * + * @var array + */ + public $_counters = []; /** * The root node of the DOM tree @@ -57,7 +59,7 @@ abstract class AbstractFrameDecorator extends Frame /** * Reflower object used to calculate frame dimensions (Strategy pattern) * - * @var \Dompdf\FrameReflower\AbstractFrameReflower + * @var AbstractFrameReflower */ protected $_reflower; @@ -76,11 +78,11 @@ abstract class AbstractFrameDecorator extends Frame private $_block_parent; /** - * First positionned parent (position: relative | absolute | fixed) + * First positioned parent (position: relative | absolute | fixed) * * @var AbstractFrameDecorator */ - private $_positionned_parent; + private $_positioned_parent; /** * Cache for the get_parent while loop results @@ -89,6 +91,27 @@ abstract class AbstractFrameDecorator extends Frame */ private $_cached_parent; + /** + * Whether generated content and counters have been set. + * + * @var bool + */ + public $content_set = false; + + /** + * Whether the frame has been split + * + * @var bool + */ + public $is_split = false; + + /** + * Whether the frame is a split-off frame + * + * @var bool + */ + public $is_split_off = false; + /** * Class constructor * @@ -104,7 +127,7 @@ abstract class AbstractFrameDecorator extends Frame } /** - * "Destructor": foribly free all references held by this object + * "Destructor": forcibly free all references held by this object * * @param bool $recursive if true, call dispose on all children */ @@ -135,12 +158,20 @@ abstract class AbstractFrameDecorator extends Frame * * @param DOMNode $node * - * @return Frame + * @return AbstractFrameDecorator */ function copy(DOMNode $node) { $frame = new Frame($node); - $frame->set_style(clone $this->_frame->get_original_style()); + $style = clone $this->_frame->get_style(); + + $style->reset(); + $frame->set_style($style); + + if ($node instanceof DOMElement && $node->hasAttribute("id")) { + $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); + $node->removeAttribute("id"); + } return Factory::decorate_frame($frame, $this->_dompdf, $this->_root); } @@ -148,20 +179,22 @@ abstract class AbstractFrameDecorator extends Frame /** * Create a deep copy: copy this node and all children * - * @return Frame + * @return AbstractFrameDecorator */ function deep_copy() { - $node = $this->_frame->get_node(); + $node = $this->_frame->get_node()->cloneNode(); + $frame = new Frame($node); + $style = clone $this->_frame->get_style(); + + $style->reset(); + $frame->set_style($style); if ($node instanceof DOMElement && $node->hasAttribute("id")) { $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); $node->removeAttribute("id"); } - $frame = new Frame($node->cloneNode()); - $frame->set_style(clone $this->_frame->get_original_style()); - $deco = Factory::decorate_frame($frame, $this->_dompdf, $this->_root); foreach ($this->get_children() as $child) { @@ -172,15 +205,41 @@ abstract class AbstractFrameDecorator extends Frame } /** - * Delegate calls to decorated frame object + * Create an anonymous child frame, inheriting styles from this frame. + * + * @param string $node_name + * @param string $display + * + * @return AbstractFrameDecorator */ + public function create_anonymous_child(string $node_name, string $display): AbstractFrameDecorator + { + $style = $this->get_style(); + $child_style = $style->get_stylesheet()->create_style(); + $child_style->set_prop("display", $display); + $child_style->inherit($style); + + $node = $this->get_node()->ownerDocument->createElement($node_name); + $frame = new Frame($node); + $frame->set_style($child_style); + + return Factory::decorate_frame($frame, $this->_dompdf, $this->_root); + } + function reset() { $this->_frame->reset(); + $this->_reflower->reset(); + $this->reset_generated_content(); + $this->revert_counter_increment(); - $this->_counters = array(); + $this->content_set = false; + $this->_counters = []; - $this->_cached_parent = null; //clear get_parent() cache + // clear parent lookup caches + $this->_cached_parent = null; + $this->_block_parent = null; + $this->_positioned_parent = null; // Reset all children foreach ($this->get_children() as $child) { @@ -188,11 +247,42 @@ abstract class AbstractFrameDecorator extends Frame } } - // Getters ----------- + /** + * If this represents a generated node then child nodes represent generated + * content. Remove the children since the content will be generated next + * time this frame is reflowed. + */ + protected function reset_generated_content(): void + { + if ($this->content_set + && $this->get_node()->nodeName === "dompdf_generated" + ) { + $content = $this->get_style()->content; + + if ($content !== "normal" && $content !== "none") { + foreach ($this->get_children() as $child) { + $this->remove_child($child); + } + } + } + } /** - * @return string + * Decrement any counters that were incremented on the current node, unless + * that node is the body. */ + protected function revert_counter_increment(): void + { + if ($this->content_set + && $this->get_node()->nodeName !== "body" + && ($decrement = $this->get_style()->counter_increment) !== "none" + ) { + $this->decrement_counters($decrement); + } + } + + // Getters ----------- + function get_id() { return $this->_frame->get_id(); @@ -206,45 +296,29 @@ abstract class AbstractFrameDecorator extends Frame return $this->_frame; } - /** - * @return DOMElement|DOMText - */ function get_node() { return $this->_frame->get_node(); } - /** - * @return Style - */ function get_style() { return $this->_frame->get_style(); } /** - * @return Style + * @deprecated */ function get_original_style() { - return $this->_frame->get_original_style(); + return $this->_frame->get_style(); } - /** - * @param integer $i - * - * @return array|float - */ function get_containing_block($i = null) { return $this->_frame->get_containing_block($i); } - /** - * @param integer $i - * - * @return array|float - */ function get_position($i = null) { return $this->_frame->get_position($i); @@ -258,110 +332,66 @@ abstract class AbstractFrameDecorator extends Frame return $this->_dompdf; } - /** - * @return float - */ - function get_margin_height() - { - return $this->_frame->get_margin_height(); - } - - /** - * @return float - */ - function get_margin_width() + public function get_margin_width(): float { return $this->_frame->get_margin_width(); } - /** - * @return array - */ - function get_content_box() + public function get_margin_height(): float + { + return $this->_frame->get_margin_height(); + } + + public function get_content_box(): array { return $this->_frame->get_content_box(); } - /** - * @return array - */ - function get_padding_box() + public function get_padding_box(): array { return $this->_frame->get_padding_box(); } - /** - * @return array - */ - function get_border_box() + public function get_border_box(): array { return $this->_frame->get_border_box(); } - /** - * @param integer $id - */ function set_id($id) { $this->_frame->set_id($id); } - /** - * @param Style $style - */ - function set_style(Style $style) + public function set_style(Style $style): void { $this->_frame->set_style($style); } - /** - * @param float $x - * @param float $y - * @param float $w - * @param float $h - */ function set_containing_block($x = null, $y = null, $w = null, $h = null) { $this->_frame->set_containing_block($x, $y, $w, $h); } - /** - * @param float $x - * @param float $y - */ function set_position($x = null, $y = null) { $this->_frame->set_position($x, $y); } - /** - * @return bool - */ function is_auto_height() { return $this->_frame->is_auto_height(); } - /** - * @return bool - */ function is_auto_width() { return $this->_frame->is_auto_width(); } - /** - * @return string - */ function __toString() { return $this->_frame->__toString(); } - /** - * @param Frame $child - * @param bool $update_node - */ function prepend_child(Frame $child, $update_node = true) { while ($child instanceof AbstractFrameDecorator) { @@ -371,10 +401,6 @@ abstract class AbstractFrameDecorator extends Frame $this->_frame->prepend_child($child, $update_node); } - /** - * @param Frame $child - * @param bool $update_node - */ function append_child(Frame $child, $update_node = true) { while ($child instanceof AbstractFrameDecorator) { @@ -384,11 +410,6 @@ abstract class AbstractFrameDecorator extends Frame $this->_frame->append_child($child, $update_node); } - /** - * @param Frame $new_child - * @param Frame $ref - * @param bool $update_node - */ function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) { while ($new_child instanceof AbstractFrameDecorator) { @@ -402,11 +423,6 @@ abstract class AbstractFrameDecorator extends Frame $this->_frame->insert_child_before($new_child, $ref, $update_node); } - /** - * @param Frame $new_child - * @param Frame $ref - * @param bool $update_node - */ function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) { $insert_frame = $new_child; @@ -422,12 +438,6 @@ abstract class AbstractFrameDecorator extends Frame $this->_frame->insert_child_after($insert_frame, $reference_frame, $update_node); } - /** - * @param Frame $child - * @param bool $update_node - * - * @return Frame - */ function remove_child(Frame $child, $update_node = true) { while ($child instanceof AbstractFrameDecorator) { @@ -543,11 +553,19 @@ abstract class AbstractFrameDecorator extends Frame } /** - * @return FrameTreeList + * @return FrameListIterator */ - function get_subtree() + public function get_children(): FrameListIterator { - return new FrameTreeList($this); + return new FrameListIterator($this); + } + + /** + * @return FrameTreeIterator + */ + function get_subtree(): FrameTreeIterator + { + return new FrameTreeIterator($this); } function set_positioner(AbstractPositioner $posn) @@ -567,7 +585,15 @@ abstract class AbstractFrameDecorator extends Frame } /** - * @return \Dompdf\FrameReflower\AbstractFrameReflower + * @return AbstractPositioner + */ + function get_positioner() + { + return $this->_positioner; + } + + /** + * @return AbstractFrameReflower */ function get_reflower() { @@ -600,6 +626,10 @@ abstract class AbstractFrameDecorator extends Frame function find_block_parent() { // Find our nearest block level parent + if (isset($this->_block_parent)) { + return $this->_block_parent; + } + $p = $this->get_parent(); while ($p) { @@ -616,12 +646,16 @@ abstract class AbstractFrameDecorator extends Frame /** * @return AbstractFrameDecorator */ - function find_positionned_parent() + function find_positioned_parent() { - // Find our nearest relative positionned parent + // Find our nearest relative positioned parent + if (isset($this->_positioned_parent)) { + return $this->_positioned_parent; + } + $p = $this->get_parent(); while ($p) { - if ($p->is_positionned()) { + if ($p->is_positioned()) { break; } @@ -629,55 +663,28 @@ abstract class AbstractFrameDecorator extends Frame } if (!$p) { - $p = $this->_root->get_first_child(); // + $p = $this->_root; } - return $this->_positionned_parent = $p; + return $this->_positioned_parent = $p; } /** - * split this frame at $child. + * Split this frame at $child. * The current frame is cloned and $child and all children following * $child are added to the clone. The clone is then passed to the * current frame's parent->split() method. * - * @param Frame $child - * @param boolean $force_pagebreak + * @param Frame|null $child + * @param bool $page_break + * @param bool $forced Whether the page break is forced. * * @throws Exception - * @return void */ - function split(Frame $child = null, $force_pagebreak = false) + public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void { - // decrement any counters that were incremented on the current node, unless that node is the body - $style = $this->_frame->get_style(); - if ( - $this->_frame->get_node()->nodeName !== "body" && - $style->counter_increment && - ($decrement = $style->counter_increment) !== "none" - ) { - $this->decrement_counters($decrement); - } - if (is_null($child)) { - // check for counter increment on :before content (always a child of the selected element @link AbstractFrameReflower::_set_content) - // this can push the current node to the next page before counter rules have bubbled up (but only if - // it's been rendered, thus the position check) - if (!$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id")) { - foreach ($this->_frame->get_children() as $child) { - if ( - $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && - $child->get_position('x') !== null - ) { - $style = $child->get_style(); - if ($style->counter_increment && ($decrement = $style->counter_increment) !== "none") { - $this->decrement_counters($decrement); - } - } - } - } - $this->get_parent()->split($this, $force_pagebreak); - + $this->get_parent()->split($this, $page_break, $forced); return; } @@ -685,104 +692,118 @@ abstract class AbstractFrameDecorator extends Frame throw new Exception("Unable to split: frame is not a child of this one."); } - $node = $this->_frame->get_node(); + $this->revert_counter_increment(); - if ($node instanceof DOMElement && $node->hasAttribute("id")) { - $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); - $node->removeAttribute("id"); + $node = $this->_frame->get_node(); + $split = $this->copy($node->cloneNode()); + + $style = $this->_frame->get_style(); + $split_style = $split->get_style(); + + // Truncate the box decoration at the split, except for the body + if ($node->nodeName !== "body") { + // Clear bottom decoration of original frame + $style->margin_bottom = 0.0; + $style->padding_bottom = 0.0; + $style->border_bottom_width = 0.0; + $style->border_bottom_left_radius = 0.0; + $style->border_bottom_right_radius = 0.0; + + // Clear top decoration of split frame + $split_style->margin_top = 0.0; + $split_style->padding_top = 0.0; + $split_style->border_top_width = 0.0; + $split_style->border_top_left_radius = 0.0; + $split_style->border_top_right_radius = 0.0; + $split_style->page_break_before = "auto"; } - $split = $this->copy($node->cloneNode()); - $split->reset(); - $split->get_original_style()->text_indent = 0; - $split->_splitted = true; + $split_style->text_indent = 0.0; + $split_style->counter_reset = "none"; + + $this->is_split = true; + $split->is_split_off = true; $split->_already_pushed = true; - // The body's properties must be kept - if ($node->nodeName !== "body") { - // Style reset on the first and second parts - $style = $this->_frame->get_style(); - $style->margin_bottom = 0; - $style->padding_bottom = 0; - $style->border_bottom = 0; - - // second - $orig_style = $split->get_original_style(); - $orig_style->text_indent = 0; - $orig_style->margin_top = 0; - $orig_style->padding_top = 0; - $orig_style->border_top = 0; - $orig_style->page_break_before = "auto"; - } - - // recalculate the float offsets after paging $this->get_parent()->insert_child_after($split, $this); + if ($this instanceof Block) { - foreach ($this->get_line_boxes() as $index => $line_box) { + // Remove the frames that will be moved to the new split node from + // the line boxes + $this->remove_frames_from_line($child); + + // recalculate the float offsets after paging + foreach ($this->get_line_boxes() as $line_box) { $line_box->get_float_offsets(); } } - // Add $frame and all following siblings to the new split node + if (!$forced) { + // Reset top margin in case of an unforced page break + // https://www.w3.org/TR/CSS21/page.html#allowed-page-breaks + $child->get_style()->margin_top = 0.0; + } + + // Add $child and all following siblings to the new split node $iter = $child; while ($iter) { $frame = $iter; $iter = $iter->get_next_sibling(); $frame->reset(); - $frame->_parent = $split; $split->append_child($frame); - - // recalculate the float offsets - if ($frame instanceof Block) { - foreach ($frame->get_line_boxes() as $index => $line_box) { - $line_box->get_float_offsets(); - } - } } - $this->get_parent()->split($split, $force_pagebreak); + $this->get_parent()->split($split, $page_break, $forced); - // If this node resets a counter save the current value to use when rendering on the next page - if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") { - $vars = preg_split('/\s+/', trim($reset), 2); - $split->_counters['__' . $vars[0]] = $this->lookup_counter_frame($vars[0])->_counters[$vars[0]]; + // Preserve the current counter values. This must be done after the + // parent split, as counters get reset on frame reset + $split->_counters = $this->_counters; + } + + /** + * @param array $counters + */ + public function reset_counters(array $counters): void + { + foreach ($counters as $id => $value) { + $this->reset_counter($id, $value); } } /** * @param string $id - * @param int $value + * @param int $value */ - function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) + public function reset_counter(string $id = self::DEFAULT_COUNTER, int $value = 0): void { - $this->get_parent()->_counters[$id] = intval($value); + $this->get_parent()->_counters[$id] = $value; } /** - * @param $counters + * @param array $counters */ - function decrement_counters($counters) + public function decrement_counters(array $counters): void { foreach ($counters as $id => $increment) { - $this->increment_counter($id, intval($increment) * -1); + $this->increment_counter($id, $increment * -1); } } /** - * @param $counters + * @param array $counters */ - function increment_counters($counters) + public function increment_counters(array $counters): void { foreach ($counters as $id => $increment) { - $this->increment_counter($id, intval($increment)); + $this->increment_counter($id, $increment); } } /** * @param string $id - * @param int $increment + * @param int $increment */ - function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) + public function increment_counter(string $id = self::DEFAULT_COUNTER, int $increment = 1): void { $counter_frame = $this->lookup_counter_frame($id); @@ -826,7 +847,7 @@ abstract class AbstractFrameDecorator extends Frame * * TODO: What version is the best : this one or the one in ListBullet ? */ - function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") + function counter_value(string $id = self::DEFAULT_COUNTER, string $type = "decimal") { $type = mb_strtolower($type); @@ -852,11 +873,11 @@ abstract class AbstractFrameDecorator extends Frame case "lower-latin": case "lower-alpha": - return chr(($value % 26) + ord('a') - 1); + return chr((($value - 1) % 26) + ord('a')); case "upper-latin": case "upper-alpha": - return chr(($value % 26) + ord('A') - 1); + return chr((($value - 1) % 26) + ord('A')); case "lower-greek": return Helpers::unichr($value + 944); @@ -866,20 +887,17 @@ abstract class AbstractFrameDecorator extends Frame } } - /** - * - */ final function position() { $this->_positioner->position($this); } /** - * @param $offset_x - * @param $offset_y - * @param bool $ignore_self + * @param float $offset_x + * @param float $offset_y + * @param bool $ignore_self */ - final function move($offset_x, $offset_y, $ignore_self = false) + final function move(float $offset_x, float $offset_y, bool $ignore_self = false): void { $this->_positioner->move($this, $offset_x, $offset_y, $ignore_self); } @@ -898,18 +916,8 @@ abstract class AbstractFrameDecorator extends Frame /** * @return array */ - final function get_min_max_width() + final public function get_min_max_width(): array { return $this->_reflower->get_min_max_width(); } - - /** - * Determine current frame width based on contents - * - * @return float - */ - final function calculate_auto_width() - { - return $this->_reflower->calculate_auto_width(); - } } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Block.php new file mode 100644 index 000000000..1fcf134d8 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Block.php @@ -0,0 +1,256 @@ +_line_boxes = [new LineBox($this)]; + $this->_cl = 0; + $this->dangling_markers = []; + } + + function reset() + { + parent::reset(); + + $this->_line_boxes = [new LineBox($this)]; + $this->_cl = 0; + $this->dangling_markers = []; + } + + /** + * @return LineBox + */ + function get_current_line_box() + { + return $this->_line_boxes[$this->_cl]; + } + + /** + * @return int + */ + function get_current_line_number() + { + return $this->_cl; + } + + /** + * @return LineBox[] + */ + function get_line_boxes() + { + return $this->_line_boxes; + } + + /** + * @param int $line_number + * @return int + */ + function set_current_line_number($line_number) + { + $line_boxes_count = count($this->_line_boxes); + $cl = max(min($line_number, $line_boxes_count), 0); + return ($this->_cl = $cl); + } + + /** + * @param int $i + */ + function clear_line($i) + { + if (isset($this->_line_boxes[$i])) { + unset($this->_line_boxes[$i]); + } + } + + /** + * @param Frame $frame + * @return LineBox|null + */ + public function add_frame_to_line(Frame $frame): ?LineBox + { + $current_line = $this->_line_boxes[$this->_cl]; + $frame->set_containing_line($current_line); + + // Inline frames are currently treated as wrappers, and are not actually + // added to the line + if ($frame instanceof Inline) { + return null; + } + + $current_line->add_frame($frame); + + $this->increase_line_width($frame->get_margin_width()); + $this->maximize_line_height($frame->get_margin_height(), $frame); + + // Add any dangling list markers to the first line box if it is inline + if ($this->_cl === 0 && $current_line->inline + && $this->dangling_markers !== [] + ) { + foreach ($this->dangling_markers as $marker) { + $current_line->add_list_marker($marker); + $this->maximize_line_height($marker->get_margin_height(), $marker); + } + + $this->dangling_markers = []; + } + + return $current_line; + } + + /** + * Remove the given frame and all following frames and lines from the block. + * + * @param Frame $frame + */ + public function remove_frames_from_line(Frame $frame): void + { + // Inline frames are not added to line boxes themselves, only their + // text frame children + $actualFrame = $frame; + while ($actualFrame !== null && $actualFrame instanceof Inline) { + $actualFrame = $actualFrame->get_first_child(); + } + + if ($actualFrame === null) { + return; + } + + // Search backwards through the lines for $frame + $frame = $actualFrame; + $i = $this->_cl; + $j = null; + + while ($i > 0) { + $line = $this->_line_boxes[$i]; + foreach ($line->get_frames() as $index => $f) { + if ($frame === $f) { + $j = $index; + break 2; + } + } + $i--; + } + + if ($j === null) { + return; + } + + // Remove all lines that follow + for ($k = $this->_cl; $k > $i; $k--) { + unset($this->_line_boxes[$k]); + } + + // Remove the line, if it is empty + if ($j > 0) { + $line->remove_frames($j); + } else { + unset($this->_line_boxes[$i]); + } + + // Reset array indices + $this->_line_boxes = array_values($this->_line_boxes); + $this->_cl = count($this->_line_boxes) - 1; + } + + /** + * @param float $w + */ + public function increase_line_width(float $w): void + { + $this->_line_boxes[$this->_cl]->w += $w; + } + + /** + * @param float $val + * @param Frame $frame + */ + public function maximize_line_height(float $val, Frame $frame): void + { + if ($val > $this->_line_boxes[$this->_cl]->h) { + $this->_line_boxes[$this->_cl]->tallest_frame = $frame; + $this->_line_boxes[$this->_cl]->h = $val; + } + } + + /** + * @param bool $br + */ + public function add_line(bool $br = false): void + { + $line = $this->_line_boxes[$this->_cl]; + + $line->br = $br; + $y = $line->y + $line->h; + + $new_line = new LineBox($this, $y); + + $this->_line_boxes[++$this->_cl] = $new_line; + } + + /** + * @param ListBullet $marker + */ + public function add_dangling_marker(ListBullet $marker): void + { + $this->dangling_markers[] = $marker; + } + + /** + * Inherit any dangling markers from the parent block. + * + * @param Block $block + */ + public function inherit_dangling_markers(self $block): void + { + if ($block->dangling_markers !== []) { + $this->dangling_markers = $block->dangling_markers; + $block->dangling_markers = []; + } + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Image.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Image.php similarity index 62% rename from library/vendor/dompdf/src/FrameDecorator/Image.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Image.php index 0dc62e966..92ac491a4 100644 --- a/library/vendor/dompdf/src/FrameDecorator/Image.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Image.php @@ -1,15 +1,14 @@ - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameDecorator; use Dompdf\Dompdf; use Dompdf\Frame; +use Dompdf\Helpers; use Dompdf\Image\Cache; /** @@ -56,18 +55,48 @@ class Image extends AbstractFrameDecorator $dompdf->getProtocol(), $dompdf->getBaseHost(), $dompdf->getBasePath(), - $dompdf + $dompdf->getOptions() ); if (Cache::is_broken($this->_image_url) && $alt = $frame->get_node()->getAttribute("alt") ) { + $fontMetrics = $dompdf->getFontMetrics(); $style = $frame->get_style(); - $style->width = (4 / 3) * $dompdf->getFontMetrics()->getTextWidth($alt, $style->font_family, $style->font_size, $style->word_spacing); - $style->height = $dompdf->getFontMetrics()->getFontHeight($style->font_family, $style->font_size); + $font = $style->font_family; + $size = $style->font_size; + $word_spacing = $style->word_spacing; + $letter_spacing = $style->letter_spacing; + + $style->width = (4 / 3) * $fontMetrics->getTextWidth($alt, $font, $size, $word_spacing, $letter_spacing); + $style->height = $fontMetrics->getFontHeight($font, $size); } } + /** + * Get the intrinsic pixel dimensions of the image. + * + * @return array Width and height as `float|int`. + */ + public function get_intrinsic_dimensions(): array + { + [$width, $height] = Helpers::dompdf_getimagesize($this->_image_url, $this->_dompdf->getHttpContext()); + + return [$width, $height]; + } + + /** + * Resample the given pixel length according to dpi. + * + * @param float|int $length + * @return float + */ + public function resample($length): float + { + $dpi = $this->_dompdf->getOptions()->getDpi(); + return ($length * 72) / $dpi; + } + /** * Return the image's url * diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Inline.php new file mode 100644 index 000000000..668d795e0 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Inline.php @@ -0,0 +1,121 @@ +get_style(); + $font = $style->font_family; + $size = $style->font_size; + $fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size); + + return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight; + } + + public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void + { + if (is_null($child)) { + $this->get_parent()->split($this, $page_break, $forced); + return; + } + + if ($child->get_parent() !== $this) { + throw new Exception("Unable to split: frame is not a child of this one."); + } + + $this->revert_counter_increment(); + $node = $this->_frame->get_node(); + $split = $this->copy($node->cloneNode()); + + $style = $this->_frame->get_style(); + $split_style = $split->get_style(); + + // Unset the current node's right style properties + $style->margin_right = 0.0; + $style->padding_right = 0.0; + $style->border_right_width = 0.0; + $style->border_top_right_radius = 0.0; + $style->border_bottom_right_radius = 0.0; + + // Unset the split node's left style properties since we don't want them + // to propagate + $split_style->margin_left = 0.0; + $split_style->padding_left = 0.0; + $split_style->border_left_width = 0.0; + $split_style->border_top_left_radius = 0.0; + $split_style->border_bottom_left_radius = 0.0; + + // If this is a generated node don't propagate the content style + if ($split->get_node()->nodeName == "dompdf_generated") { + $split_style->content = "normal"; + } + + //On continuation of inline element on next line, + //don't repeat non-horizontally repeatable background images + //See e.g. in testcase image_variants, long descriptions + if (($url = $style->background_image) && $url !== "none" + && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-x" + ) { + $split_style->background_image = "none"; + } + + $this->get_parent()->insert_child_after($split, $this); + + // Add $child and all following siblings to the new split node + $iter = $child; + while ($iter) { + $frame = $iter; + $iter = $iter->get_next_sibling(); + $frame->reset(); + $split->append_child($frame); + } + + $parent = $this->get_parent(); + + if ($page_break) { + $parent->split($split, $page_break, $forced); + } elseif ($parent instanceof Inline) { + $parent->split($split); + } + } + +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php new file mode 100644 index 000000000..703f46767 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBullet.php @@ -0,0 +1,117 @@ +_frame->get_style(); + + if ($style->list_style_type === "none") { + return 0.0; + } + + return $style->font_size * self::BULLET_SIZE; + } + + /** + * Get the height of the bullet symbol. + * + * @return float + */ + public function get_height(): float + { + $style = $this->_frame->get_style(); + + if ($style->list_style_type === "none") { + return 0.0; + } + + return $style->font_size * self::BULLET_SIZE; + } + + /** + * Get the width of the bullet, including indentation. + */ + public function get_margin_width(): float + { + $style = $this->get_style(); + + if ($style->list_style_type === "none") { + return 0.0; + } + + return $style->font_size * (self::BULLET_SIZE + self::MARKER_INDENT); + } + + /** + * Get the line height for the bullet. + * + * This increases the height of the corresponding line box when necessary. + */ + public function get_margin_height(): float + { + $style = $this->get_style(); + + if ($style->list_style_type === "none") { + return 0.0; + } + + // TODO: This is a copy of `FrameDecorator\Text::get_margin_height()` + // Would be nice to properly refactor that at some point + $font = $style->font_family; + $size = $style->font_size; + $fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size); + + return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight; + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBulletImage.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBulletImage.php new file mode 100644 index 000000000..d921929c2 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/ListBulletImage.php @@ -0,0 +1,112 @@ +get_style(); + $url = $style->list_style_image; + $frame->get_node()->setAttribute("src", $url); + $this->_img = new Image($frame, $dompdf); + parent::__construct($this->_img, $dompdf); + + $url = $this->_img->get_image_url(); + + if (Cache::is_broken($url)) { + $this->_width = parent::get_width(); + $this->_height = parent::get_height(); + } else { + // Resample the bullet image to be consistent with 'auto' sized images + [$width, $height] = $this->_img->get_intrinsic_dimensions(); + $this->_width = $this->_img->resample($width); + $this->_height = $this->_img->resample($height); + } + } + + public function get_width(): float + { + return $this->_width; + } + + public function get_height(): float + { + return $this->_height; + } + + public function get_margin_width(): float + { + $style = $this->get_style(); + return $this->_width + $style->font_size * self::MARKER_INDENT; + } + + public function get_margin_height(): float + { + $fontMetrics = $this->_dompdf->getFontMetrics(); + $style = $this->get_style(); + $font = $style->font_family; + $size = $style->font_size; + $fontHeight = $fontMetrics->getFontHeight($font, $size); + $baseline = $fontMetrics->getFontBaseline($font, $size); + + // This is the same factor as used in + // `FrameDecorator\Text::get_margin_height()` + $f = $style->line_height / ($size > 0 ? $size : 1); + + // FIXME: Tries to approximate replacing the space above the font + // baseline with the image + return $f * ($fontHeight - $baseline) + $this->_height; + } + + /** + * Return image url + * + * @return string + */ + function get_image_url() + { + return $this->_img->get_image_url(); + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/NullFrameDecorator.php similarity index 87% rename from library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/NullFrameDecorator.php index e3457cfe7..f08381675 100644 --- a/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/NullFrameDecorator.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameDecorator; diff --git a/library/vendor/dompdf/src/FrameDecorator/Page.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Page.php similarity index 62% rename from library/vendor/dompdf/src/FrameDecorator/Page.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Page.php index 39921aba8..25ef2408f 100644 --- a/library/vendor/dompdf/src/FrameDecorator/Page.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Page.php @@ -1,13 +1,11 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameDecorator; -use Dompdf\Css\Style; use Dompdf\Dompdf; use Dompdf\Helpers; use Dompdf\Frame; @@ -16,18 +14,18 @@ use Dompdf\Renderer; /** * Decorates frames for page layout * - * @access private * @package dompdf */ class Page extends AbstractFrameDecorator { - /** - * y value of bottom page margin + * The y value of the bottom edge of the page area. + * + * https://www.w3.org/TR/CSS21/page.html#page-margins * * @var float */ - protected $_bottom_page_margin; + protected $bottom_page_edge; /** * Flag indicating page is full. @@ -55,7 +53,7 @@ class Page extends AbstractFrameDecorator * * @var array */ - protected $_floating_frames = array(); + protected $_floating_frames = []; //........................................................................ @@ -70,7 +68,7 @@ class Page extends AbstractFrameDecorator parent::__construct($frame, $dompdf); $this->_page_full = false; $this->_in_table = 0; - $this->_bottom_page_margin = null; + $this->bottom_page_edge = null; } /** @@ -94,20 +92,16 @@ class Page extends AbstractFrameDecorator } /** - * Set the frame's containing block. Overridden to set $this->_bottom_page_margin. - * - * @param float $x - * @param float $y - * @param float $w - * @param float $h + * Calculate the bottom edge of the page area after margins have been + * applied for the current page. */ - function set_containing_block($x = null, $y = null, $w = null, $h = null) + public function calculate_bottom_page_edge(): void { - parent::set_containing_block($x, $y, $w, $h); - //$w = $this->get_containing_block("w"); - if (isset($h)) { - $this->_bottom_page_margin = $h; - } // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w); + [, , , $cbh] = $this->get_containing_block(); + $style = $this->get_style(); + $margin_bottom = (float) $style->length_in_pt($style->margin_bottom, $cbh); + + $this->bottom_page_edge = $cbh - $margin_bottom; } /** @@ -125,7 +119,7 @@ class Page extends AbstractFrameDecorator */ function next_page() { - $this->_floating_frames = array(); + $this->_floating_frames = []; $this->_renderer->new_page(); $this->_page_full = false; } @@ -158,64 +152,70 @@ class Page extends AbstractFrameDecorator /** * Check if a forced page break is required before $frame. This uses the - * frame's page_break_before property as well as the preceeding frame's + * frame's page_break_before property as well as the preceding frame's * page_break_after property. * * @link http://www.w3.org/TR/CSS21/page.html#forced * - * @param Frame $frame the frame to check + * @param AbstractFrameDecorator $frame the frame to check * - * @return bool true if a page break occured + * @return bool true if a page break occurred */ function check_forced_page_break(Frame $frame) { - // Skip check if page is already split - if ($this->_page_full) { - return null; - } - - $block_types = array("block", "list-item", "table", "inline"); - $page_breaks = array("always", "left", "right"); - - $style = $frame->get_style(); - - if (!in_array($style->display, $block_types)) { + // Skip check if page is already split and for the body + if ($this->_page_full || $frame->get_node()->nodeName === "body") { return false; } - // Find the previous block-level sibling - $prev = $frame->get_prev_sibling(); + $page_breaks = ["always", "left", "right"]; + $style = $frame->get_style(); - while ($prev && !in_array($prev->get_style()->display, $block_types)) { + if (($frame->is_block_level() || $style->display === "table-row") + && in_array($style->page_break_before, $page_breaks, true) + ) { + // Prevent cascading splits + $frame->split(null, true, true); + $style->page_break_before = "auto"; + $this->_page_full = true; + $frame->_already_pushed = true; + + return true; + } + + // Find the preceding block-level sibling (or table row). Inline + // elements are treated as if wrapped in an anonymous block container + // here. See https://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level + $prev = $frame->get_prev_sibling(); + while ($prev && (($prev->is_text_node() && $prev->get_node()->nodeValue === "") + || $prev->get_node()->nodeName === "bullet") + ) { $prev = $prev->get_prev_sibling(); } - if (in_array($style->page_break_before, $page_breaks)) { - // Prevent cascading splits - $frame->split(null, true); - // We have to grab the style again here because split() resets - // $frame->style to the frame's original style. - $frame->get_style()->page_break_before = "auto"; - $this->_page_full = true; - $frame->_already_pushed = true; + if ($prev && ($prev->is_block_level() || $prev->get_style()->display === "table-row")) { + if (in_array($prev->get_style()->page_break_after, $page_breaks, true)) { + // Prevent cascading splits + $frame->split(null, true, true); + $prev->get_style()->page_break_after = "auto"; + $this->_page_full = true; + $frame->_already_pushed = true; - return true; - } + return true; + } - if ($prev && in_array($prev->get_style()->page_break_after, $page_breaks)) { - // Prevent cascading splits - $frame->split(null, true); - $prev->get_style()->page_break_after = "auto"; - $this->_page_full = true; - $frame->_already_pushed = true; - - return true; - } - - if ($prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body") { $prev_last_child = $prev->get_last_child(); - if (in_array($prev_last_child->get_style()->page_break_after, $page_breaks)) { - $frame->split(null, true); + while ($prev_last_child && (($prev_last_child->is_text_node() && $prev_last_child->get_node()->nodeValue === "") + || $prev_last_child->get_node()->nodeName === "bullet") + ) { + $prev_last_child = $prev_last_child->get_prev_sibling(); + } + + if ($prev_last_child + && $prev_last_child->is_block_level() + && in_array($prev_last_child->get_style()->page_break_after, $page_breaks, true) + ) { + $frame->split(null, true, true); $prev_last_child->get_style()->page_break_after = "auto"; $this->_page_full = true; $frame->_already_pushed = true; @@ -227,16 +227,45 @@ class Page extends AbstractFrameDecorator return false; } + /** + * Check for a gap between the top content edge of a frame and its child + * content. + * + * Additionally, the top margin, border, and padding of the frame must fit + * on the current page. + * + * @param float $childPos The top margin or line-box edge of the child content. + * @param Frame $frame The parent frame to check. + * @return bool + */ + protected function hasGap(float $childPos, Frame $frame): bool + { + $style = $frame->get_style(); + $cbw = $frame->get_containing_block("w"); + $contentEdge = $frame->get_position("y") + (float) $style->length_in_pt([ + $style->margin_top, + $style->border_top_width, + $style->padding_top + ], $cbw); + + return Helpers::lengthGreater($childPos, $contentEdge) + && Helpers::lengthLessOrEqual($contentEdge, $this->bottom_page_edge); + } + /** * Determine if a page break is allowed before $frame * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks * * In the normal flow, page breaks can occur at the following places: * - * 1. In the vertical margin between block boxes. When a page - * break occurs here, the used values of the relevant - * 'margin-top' and 'margin-bottom' properties are set to '0'. - * 2. Between line boxes inside a block box. + * 1. In the vertical margin between block boxes. When an + * unforced page break occurs here, the used values of the + * relevant 'margin-top' and 'margin-bottom' properties are set + * to '0'. When a forced page break occurs here, the used value + * of the relevant 'margin-bottom' property is set to '0'; the + * relevant 'margin-top' used value may either be set to '0' or + * retained. + * 2. Between line boxes inside a block container box. * 3. Between the content edge of a block container box and the * outer edges of its child content (margin edges of block-level * children or line box edges for inline-level children) if there @@ -245,49 +274,45 @@ class Page extends AbstractFrameDecorator * These breaks are subject to the following rules: * * * Rule A: Breaking at (1) is allowed only if the - * 'page-break-after' and 'page-break-before' properties of - * all the elements generating boxes that meet at this margin - * allow it, which is when at least one of them has the value - * 'always', 'left', or 'right', or when all of them are - * 'auto'. + * 'page-break-after' and 'page-break-before' properties of all + * the elements generating boxes that meet at this margin allow + * it, which is when at least one of them has the value + * 'always', 'left', or 'right', or when all of them are 'auto'. * - * * Rule B: However, if all of them are 'auto' and the - * nearest common ancestor of all the elements has a - * 'page-break-inside' value of 'avoid', then breaking here is - * not allowed. + * * Rule B: However, if all of them are 'auto' and a common + * ancestor of all the elements has a 'page-break-inside' value + * of 'avoid', then breaking here is not allowed. * - * * Rule C: Breaking at (2) is allowed only if the number of - * line boxes between the break and the start of the enclosing - * block box is the value of 'orphans' or more, and the number - * of line boxes between the break and the end of the box is - * the value of 'widows' or more. + * * Rule C: Breaking at (2) is allowed only if the number of line + * boxes between the break and the start of the enclosing block + * box is the value of 'orphans' or more, and the number of line + * boxes between the break and the end of the box is the value + * of 'widows' or more. * - * * Rule D: In addition, breaking at (2) is allowed only if - * the 'page-break-inside' property is 'auto'. + * * Rule D: In addition, breaking at (2) or (3) is allowed only + * if the 'page-break-inside' property of the element and all + * its ancestors is 'auto'. * - * If the above doesn't provide enough break points to keep - * content from overflowing the page boxes, then rules B and D are + * If the above does not provide enough break points to keep content + * from overflowing the page boxes, then rules A, B and D are * dropped in order to find additional breakpoints. * - * If that still does not lead to sufficient break points, rules A - * and C are dropped as well, to find still more break points. + * If that still does not lead to sufficient break points, rule C is + * dropped as well, to find still more break points. * - * We will also allow breaks between table rows. However, when - * splitting a table, the table headers should carry over to the - * next page (but they don't yet). + * We also allow breaks between table rows. * - * @param Frame $frame the frame to check + * @param AbstractFrameDecorator $frame the frame to check * * @return bool true if a break is allowed, false otherwise */ protected function _page_break_allowed(Frame $frame) { - $block_types = array("block", "list-item", "table", "-dompdf-image"); Helpers::dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")"); $display = $frame->get_style()->display; // Block Frames (1): - if (in_array($display, $block_types)) { + if ($frame->is_block_level() || $display === "-dompdf-image") { // Avoid breaks within table-cells if ($this->_in_table > ($display === "table" ? 1 : 0)) { @@ -296,34 +321,42 @@ class Page extends AbstractFrameDecorator return false; } - // Rules A & B - + // Rule A if ($frame->get_style()->page_break_before === "avoid") { Helpers::dompdf_debug("page-break", "before: avoid"); return false; } - // Find the preceeding block-level sibling + // Find the preceding block-level sibling. Inline elements are + // treated as if wrapped in an anonymous block container here. See + // https://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level $prev = $frame->get_prev_sibling(); - while ($prev && !in_array($prev->get_style()->display, $block_types)) { + while ($prev && (($prev->is_text_node() && $prev->get_node()->nodeValue === "") + || $prev->get_node()->nodeName === "bullet") + ) { $prev = $prev->get_prev_sibling(); } // Does the previous element allow a page break after? - if ($prev && $prev->get_style()->page_break_after === "avoid") { + if ($prev && ($prev->is_block_level() || $prev->get_style()->display === "-dompdf-image") + && $prev->get_style()->page_break_after === "avoid" + ) { Helpers::dompdf_debug("page-break", "after: avoid"); return false; } - // If both $prev & $frame have the same parent, check the parent's - // page_break_inside property. + // Rules B & D $parent = $frame->get_parent(); - if ($prev && $parent && $parent->get_style()->page_break_inside === "avoid") { - Helpers::dompdf_debug("page-break", "parent inside: avoid"); + $p = $parent; + while ($p) { + if ($p->get_style()->page_break_inside === "avoid") { + Helpers::dompdf_debug("page-break", "parent->inside: avoid"); - return false; + return false; + } + $p = $p->find_block_parent(); } // To prevent cascading page breaks when a top-level element has @@ -336,14 +369,11 @@ class Page extends AbstractFrameDecorator return false; } - // If the frame is the first block-level frame, only allow a page - // break if there is a (non-zero) gap between the frame and its - // parent - if (!$prev && $parent) { - Helpers::dompdf_debug("page-break", "First block level frame, checking gap"); + // Check for a possible type (3) break + if (!$prev && $parent && !$this->hasGap($frame->get_position("y"), $parent)) { + Helpers::dompdf_debug("page-break", "First block-level frame, no gap"); - return $frame->get_style()->length_in_pt($frame->get_style()->margin_top) != 0 - || $parent->get_style()->length_in_pt($parent->get_style()->padding_top) != 0; + return false; } Helpers::dompdf_debug("page-break", "block: break allowed"); @@ -352,7 +382,7 @@ class Page extends AbstractFrameDecorator } // Inline frames (2): else { - if (in_array($display, Style::$INLINE_TYPES)) { + if ($frame->is_inline_level()) { // Avoid breaks within table-cells if ($this->_in_table) { @@ -363,7 +393,18 @@ class Page extends AbstractFrameDecorator // Rule C $block_parent = $frame->find_block_parent(); - if (count($block_parent->get_line_boxes()) < $frame->get_style()->orphans) { + $parent_style = $block_parent->get_style(); + $line = $block_parent->get_current_line_box(); + $line_count = count($block_parent->get_line_boxes()); + $line_number = $frame->get_containing_line() && empty($line->get_frames()) + ? $line_count - 1 + : $line_count; + + // The line number of the frame can be less than the current + // number of line boxes, in case we are backtracking. As long as + // we are not checking for widows yet, just checking against the + // number of line boxes is sufficient in most cases, though. + if ($line_number <= $parent_style->orphans) { Helpers::dompdf_debug("page-break", "orphans"); return false; @@ -398,11 +439,6 @@ class Page extends AbstractFrameDecorator return false; } - // Skip breaks on empty text nodes - if ($frame->is_text_node() && $frame->get_node()->nodeValue == "") { - return false; - } - Helpers::dompdf_debug("page-break", "inline: break allowed"); return true; @@ -410,8 +446,51 @@ class Page extends AbstractFrameDecorator // Table-rows } else { if ($display === "table-row") { - // Simply check if the parent table's page_break_inside property is - // not 'avoid' + + // If this is a nested table, prevent the page from breaking + if ($this->_in_table > 1) { + Helpers::dompdf_debug("page-break", "table: nested table"); + + return false; + } + + // Rule A (table row) + if ($frame->get_style()->page_break_before === "avoid") { + Helpers::dompdf_debug("page-break", "before: avoid"); + + return false; + } + + // Find the preceding row + $prev = $frame->get_prev_sibling(); + + if (!$prev) { + $prev_group = $frame->get_parent()->get_prev_sibling(); + + if ($prev_group + && in_array($prev_group->get_style()->display, Table::ROW_GROUPS, true) + ) { + $prev = $prev_group->get_last_child(); + } + } + + // Check if a page break is allowed after the preceding row + if ($prev && $prev->get_style()->page_break_after === "avoid") { + Helpers::dompdf_debug("page-break", "after: avoid"); + + return false; + } + + // Avoid breaking before the first row of a table + if (!$prev) { + Helpers::dompdf_debug("page-break", "table: first-row"); + + return false; + } + + // Rule B (table row) + // Check if the page_break_inside property is not 'avoid' + // for the parent table or any of its ancestors $table = Table::find_parent_table($frame); $p = $table; @@ -424,38 +503,23 @@ class Page extends AbstractFrameDecorator $p = $p->find_block_parent(); } - // Avoid breaking before the first row of a table - if ($table && $table->get_first_child() === $frame || $table->get_first_child()->get_first_child() === $frame) { - Helpers::dompdf_debug("page-break", "table: first-row"); - - return false; - } - - // If this is a nested table, prevent the page from breaking - if ($this->_in_table > 1) { - Helpers::dompdf_debug("page-break", "table: nested table"); - - return false; - } - - Helpers::dompdf_debug("page-break", "table-row/row-groups: break allowed"); + Helpers::dompdf_debug("page-break", "table-row: break allowed"); return true; } else { - if (in_array($display, Table::$ROW_GROUPS)) { + if (in_array($display, Table::ROW_GROUPS, true)) { // Disallow breaks at row-groups: only split at row boundaries return false; } else { - Helpers::dompdf_debug("page-break", "? " . $frame->get_style()->display . ""); + Helpers::dompdf_debug("page-break", "? " . $display); return false; } } } } - } /** @@ -463,13 +527,16 @@ class Page extends AbstractFrameDecorator * the frame tree is modified so that a page break occurs in the * correct location. * - * @param Frame $frame the frame to check + * @param AbstractFrameDecorator $frame the frame to check * * @return bool */ function check_page_break(Frame $frame) { - if ($this->_page_full || $frame->_already_pushed) { + if ($this->_page_full || $frame->_already_pushed + // Never check for breaks on empty text nodes + || ($frame->is_text_node() && $frame->get_node()->nodeValue === "") + ) { return false; } @@ -497,13 +564,14 @@ class Page extends AbstractFrameDecorator // If a split is to occur here, then the bottom margins & paddings of all // parents of $frame must fit on the page as well: $p = $frame->get_parent(); - while ($p) { - $max_y += (float) $p->get_style()->computed_bottom_spacing(); + while ($p && $p !== $this) { + $cbw = $p->get_containing_block("w"); + $max_y += (float) $p->get_style()->computed_bottom_spacing($cbw); $p = $p->get_parent(); } // Check if $frame flows off the page - if ($max_y <= $this->_bottom_page_margin) { + if (Helpers::lengthLessOrEqual($max_y, $this->bottom_page_edge)) { // no: do nothing return false; } @@ -557,12 +625,18 @@ class Page extends AbstractFrameDecorator break; } - if ($next = $iter->get_prev_sibling()) { + $next = $iter->get_prev_sibling(); + // Skip empty text nodes + while ($next && $next->is_text_node() && $next->get_node()->nodeValue === "") { + $next = $next->get_prev_sibling(); + } + + if ($next) { Helpers::dompdf_debug("page-break", "following prev sibling."); if ($next->is_table() && !$iter->is_table()) { $this->_in_table++; - } else if (!$next->is_table() && $iter->is_table()) { + } elseif (!$next->is_table() && $iter->is_table()) { $this->_in_table--; } @@ -616,11 +690,7 @@ class Page extends AbstractFrameDecorator //........................................................................ - /** - * @param Frame|null $frame - * @param bool $force_pagebreak - */ - function split(Frame $frame = null, $force_pagebreak = false) + public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void { // Do nothing } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Table.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Table.php new file mode 100644 index 000000000..2770cbecb --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Table.php @@ -0,0 +1,343 @@ +_cellmap = new Cellmap($this); + + if ($frame->get_style()->table_layout === "fixed") { + $this->_cellmap->set_layout_fixed(true); + } + + $this->_headers = []; + $this->_footers = []; + } + + public function reset() + { + parent::reset(); + $this->_cellmap->reset(); + $this->_headers = []; + $this->_footers = []; + $this->_reflower->reset(); + } + + //........................................................................ + + /** + * Split the table at $row. $row and all subsequent rows will be + * added to the clone. This method is overridden in order to remove + * frames from the cellmap properly. + */ + public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void + { + if (is_null($child)) { + parent::split($child, $page_break, $forced); + return; + } + + // If $child is a header or if it is the first non-header row, do + // not duplicate headers, simply move the table to the next page. + if (count($this->_headers) + && !in_array($child, $this->_headers, true) + && !in_array($child->get_prev_sibling(), $this->_headers, true) + ) { + $first_header = null; + + // Insert copies of the table headers before $child + foreach ($this->_headers as $header) { + + $new_header = $header->deep_copy(); + + if (is_null($first_header)) { + $first_header = $new_header; + } + + $this->insert_child_before($new_header, $child); + } + + parent::split($first_header, $page_break, $forced); + + } elseif (in_array($child->get_style()->display, self::ROW_GROUPS, true)) { + + // Individual rows should have already been handled + parent::split($child, $page_break, $forced); + + } else { + + $iter = $child; + + while ($iter) { + $this->_cellmap->remove_row($iter); + $iter = $iter->get_next_sibling(); + } + + parent::split($child, $page_break, $forced); + } + } + + public function copy(DOMNode $node) + { + $deco = parent::copy($node); + + // In order to keep columns' widths through pages + $deco->_cellmap->set_columns($this->_cellmap->get_columns()); + $deco->_cellmap->lock_columns(); + + return $deco; + } + + /** + * Static function to locate the parent table of a frame + * + * @param Frame $frame + * + * @return Table the table that is an ancestor of $frame + */ + public static function find_parent_table(Frame $frame) + { + while ($frame = $frame->get_parent()) { + if ($frame->is_table()) { + break; + } + } + + return $frame; + } + + /** + * Return this table's Cellmap + * + * @return Cellmap + */ + public function get_cellmap() + { + return $this->_cellmap; + } + + //........................................................................ + + /** + * Check for text nodes between valid table children that only contain white + * space, except if white space is to be preserved. + * + * @param AbstractFrameDecorator $frame + * + * @return bool + */ + private function isEmptyTextNode(AbstractFrameDecorator $frame): bool + { + // This is based on the white-space pattern in `FrameReflower\Text`, + // i.e. only match on collapsible white space + $wsPattern = '/^[^\S\xA0\x{202F}\x{2007}]*$/u'; + $validChildOrNull = function ($frame) { + return $frame === null + || in_array($frame->get_style()->display, self::VALID_CHILDREN, true); + }; + + return $frame instanceof Text + && !$frame->is_pre() + && preg_match($wsPattern, $frame->get_text()) + && $validChildOrNull($frame->get_prev_sibling()) + && $validChildOrNull($frame->get_next_sibling()); + } + + /** + * Restructure tree so that the table has the correct structure. Misplaced + * children are appropriately wrapped in anonymous row groups, rows, and + * cells. + * + * https://www.w3.org/TR/CSS21/tables.html#anonymous-boxes + */ + public function normalize(): void + { + $column_caption = ["table-column-group", "table-column", "table-caption"]; + $children = iterator_to_array($this->get_children()); + $tbody = null; + + foreach ($children as $child) { + $display = $child->get_style()->display; + + if (in_array($display, self::ROW_GROUPS, true)) { + // Reset anonymous tbody + $tbody = null; + + // Add headers and footers + if ($display === "table-header-group") { + $this->_headers[] = $child; + } elseif ($display === "table-footer-group") { + $this->_footers[] = $child; + } + continue; + } + + if (in_array($display, $column_caption, true)) { + continue; + } + + // Remove empty text nodes between valid children + if ($this->isEmptyTextNode($child)) { + $this->remove_child($child); + continue; + } + + // Catch consecutive misplaced frames within a single anonymous group + if ($tbody === null) { + $tbody = $this->create_anonymous_child("tbody", "table-row-group"); + $this->insert_child_before($tbody, $child); + } + + $tbody->append_child($child); + } + + // Handle empty table: Make sure there is at least one row group + if (!$this->get_first_child()) { + $tbody = $this->create_anonymous_child("tbody", "table-row-group"); + $this->append_child($tbody); + } + + foreach ($this->get_children() as $child) { + $display = $child->get_style()->display; + + if (in_array($display, self::ROW_GROUPS, true)) { + $this->normalizeRowGroup($child); + } + } + } + + private function normalizeRowGroup(AbstractFrameDecorator $frame): void + { + $children = iterator_to_array($frame->get_children()); + $tr = null; + + foreach ($children as $child) { + $display = $child->get_style()->display; + + if ($display === "table-row") { + // Reset anonymous tr + $tr = null; + continue; + } + + // Remove empty text nodes between valid children + if ($this->isEmptyTextNode($child)) { + $frame->remove_child($child); + continue; + } + + // Catch consecutive misplaced frames within a single anonymous row + if ($tr === null) { + $tr = $frame->create_anonymous_child("tr", "table-row"); + $frame->insert_child_before($tr, $child); + } + + $tr->append_child($child); + } + + // Handle empty row group: Make sure there is at least one row + if (!$frame->get_first_child()) { + $tr = $frame->create_anonymous_child("tr", "table-row"); + $frame->append_child($tr); + } + + foreach ($frame->get_children() as $child) { + $this->normalizeRow($child); + } + } + + private function normalizeRow(AbstractFrameDecorator $frame): void + { + $children = iterator_to_array($frame->get_children()); + $td = null; + + foreach ($children as $child) { + $display = $child->get_style()->display; + + if ($display === "table-cell") { + // Reset anonymous td + $td = null; + continue; + } + + // Remove empty text nodes between valid children + if ($this->isEmptyTextNode($child)) { + $frame->remove_child($child); + continue; + } + + // Catch consecutive misplaced frames within a single anonymous cell + if ($td === null) { + $td = $frame->create_anonymous_child("td", "table-cell"); + $frame->insert_child_before($td, $child); + } + + $td->append_child($child); + } + + // Handle empty row: Make sure there is at least one cell + if (!$frame->get_first_child()) { + $td = $frame->create_anonymous_child("td", "table-cell"); + $frame->append_child($td); + } + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php similarity index 92% rename from library/vendor/dompdf/src/FrameDecorator/TableCell.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php index 9bf77258e..d382164a3 100644 --- a/library/vendor/dompdf/src/FrameDecorator/TableCell.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableCell.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameDecorator; @@ -32,7 +31,7 @@ class TableCell extends BlockFrameDecorator function __construct(Frame $frame, Dompdf $dompdf) { parent::__construct($frame, $dompdf); - $this->_resolved_borders = array(); + $this->_resolved_borders = []; $this->_content_height = 0; } @@ -41,7 +40,7 @@ class TableCell extends BlockFrameDecorator function reset() { parent::reset(); - $this->_resolved_borders = array(); + $this->_resolved_borders = []; $this->_content_height = 0; $this->_frame->reset(); } @@ -69,19 +68,19 @@ class TableCell extends BlockFrameDecorator { $style = $this->get_style(); $v_space = (float)$style->length_in_pt( - array( + [ $style->margin_top, $style->padding_top, $style->border_top_width, $style->border_bottom_width, $style->padding_bottom, $style->margin_bottom - ), + ], (float)$style->length_in_pt($style->height) ); $new_height = $height - $v_space; - $style->height = $new_height; + $style->set_used("height", $new_height); if ($new_height > $this->_content_height) { $y_offset = 0; diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php new file mode 100644 index 000000000..ba985c916 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/TableRow.php @@ -0,0 +1,28 @@ + + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameDecorator; @@ -32,22 +31,20 @@ class TableRowGroup extends AbstractFrameDecorator } /** - * Override split() to remove all child rows and this element from the cellmap - * - * @param Frame $child - * @param bool $force_pagebreak - * - * @return void + * Split the row group at the given child and remove all subsequent child + * rows and all subsequent row groups from the cellmap. */ - function split(Frame $child = null, $force_pagebreak = false) + public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void { if (is_null($child)) { - parent::split(); + parent::split($child, $page_break, $forced); return; } // Remove child & all subsequent rows from the cellmap - $cellmap = $this->get_parent()->get_cellmap(); + /** @var Table $parent */ + $parent = $this->get_parent(); + $cellmap = $parent->get_cellmap(); $iter = $child; while ($iter) { @@ -55,16 +52,23 @@ class TableRowGroup extends AbstractFrameDecorator $iter = $iter->get_next_sibling(); } + // Remove all subsequent row groups from the cellmap + $iter = $this->get_next_sibling(); + + while ($iter) { + $cellmap->remove_row_group($iter); + $iter = $iter->get_next_sibling(); + } + // If we are splitting at the first child remove the // table-row-group from the cellmap as well if ($child === $this->get_first_child()) { $cellmap->remove_row_group($this); - parent::split(); + parent::split(null, $page_break, $forced); return; } $cellmap->update_row_group($this, $child->get_prev_sibling()); - parent::split($child); + parent::split($child, $page_break, $forced); } } - diff --git a/library/vendor/dompdf/src/FrameDecorator/Text.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Text.php similarity index 58% rename from library/vendor/dompdf/src/FrameDecorator/Text.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Text.php index b1c1ea7a0..9d6693464 100644 --- a/library/vendor/dompdf/src/FrameDecorator/Text.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameDecorator/Text.php @@ -1,10 +1,7 @@ - * @author Brian Sweeney - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameDecorator; @@ -16,14 +13,14 @@ use Dompdf\Exception; /** * Decorates Frame objects for text layout * - * @access private * @package dompdf */ class Text extends AbstractFrameDecorator { - - // protected members - protected $_text_spacing; + /** + * @var float + */ + protected $text_spacing; /** * Text constructor. @@ -38,23 +35,23 @@ class Text extends AbstractFrameDecorator } parent::__construct($frame, $dompdf); - $this->_text_spacing = null; + $this->text_spacing = 0.0; } function reset() { parent::reset(); - $this->_text_spacing = null; + $this->text_spacing = 0.0; } // Accessor methods /** - * @return null + * @return float */ - function get_text_spacing() + public function get_text_spacing(): float { - return $this->_text_spacing; + return $this->text_spacing; } /** @@ -82,86 +79,74 @@ class Text extends AbstractFrameDecorator //........................................................................ /** - * Vertical margins & padding do not apply to text frames + * Vertical padding, border, and margin do not apply when determining the + * height for inline frames. * - * http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced: + * http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced * * The vertical padding, border and margin of an inline, non-replaced box * start at the top and bottom of the content area, not the * 'line-height'. But only the 'line-height' is used to calculate the * height of the line box. * - * @return float|int + * @return float */ - function get_margin_height() + public function get_margin_height(): float { - // This function is called in add_frame_to_line() and is used to - // determine the line height, so we actually want to return the - // 'line-height' property, not the actual margin box - $style = $this->get_parent()->get_style(); + // This function is also called in add_frame_to_line() and is used to + // determine the line height + $style = $this->get_style(); $font = $style->font_family; $size = $style->font_size; + $fontHeight = $this->_dompdf->getFontMetrics()->getFontHeight($font, $size); - /* - Helpers::pre_r('-----'); - Helpers::pre_r($style->line_height); - Helpers::pre_r($style->font_size); - Helpers::pre_r($this->_dompdf->getFontMetrics()->getFontHeight($font, $size)); - Helpers::pre_r(($style->line_height / $size) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size)); - */ - - return ($style->line_height / ($size > 0 ? $size : 1)) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size); + return ($style->line_height / ($size > 0 ? $size : 1)) * $fontHeight; } - /** - * @return array - */ - function get_padding_box() + public function get_padding_box(): array { + $style = $this->_frame->get_style(); $pb = $this->_frame->get_padding_box(); - $pb[3] = $pb["h"] = $this->_frame->get_style()->height; - + $pb[3] = $pb["h"] = (float) $style->length_in_pt($style->height); return $pb; } /** - * @param $spacing + * @param float $spacing */ - function set_text_spacing($spacing) + public function set_text_spacing(float $spacing): void { - $style = $this->_frame->get_style(); - - $this->_text_spacing = $spacing; - $char_spacing = (float)$style->length_in_pt($style->letter_spacing); - - // Re-adjust our width to account for the change in spacing - $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing); + $this->text_spacing = $spacing; + $this->recalculate_width(); } /** - * Recalculate the text width + * Recalculate the text width * * @return float */ - function recalculate_width() + public function recalculate_width(): float { + $fontMetrics = $this->_dompdf->getFontMetrics(); $style = $this->get_style(); $text = $this->get_text(); - $size = $style->font_size; $font = $style->font_family; - $word_spacing = (float)$style->length_in_pt($style->word_spacing); - $char_spacing = (float)$style->length_in_pt($style->letter_spacing); + $size = $style->font_size; + $word_spacing = $this->text_spacing + $style->word_spacing; + $letter_spacing = $style->letter_spacing; + $text_width = $fontMetrics->getTextWidth($text, $font, $size, $word_spacing, $letter_spacing); - return $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + $style->set_used("width", $text_width); + return $text_width; } // Text manipulation methods /** - * split the text in this frame at the offset specified. The remaining - * text is added a sibling frame following this one and is returned. + * Split the text in this frame at the offset specified. The remaining + * text is added as a sibling frame following this one and is returned. * - * @param $offset + * @param int $offset * @return Frame|null */ function split_text($offset) @@ -171,7 +156,10 @@ class Text extends AbstractFrameDecorator } $split = $this->_frame->get_node()->splitText($offset); - + if ($split === false) { + return null; + } + $deco = $this->copy($split); $p = $this->get_parent(); @@ -185,8 +173,8 @@ class Text extends AbstractFrameDecorator } /** - * @param $offset - * @param $count + * @param int $offset + * @param int $count */ function delete_text($offset, $count) { @@ -194,7 +182,7 @@ class Text extends AbstractFrameDecorator } /** - * @param $text + * @param string $text */ function set_text($text) { diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php new file mode 100644 index 000000000..fa0cc511b --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/AbstractFrameReflower.php @@ -0,0 +1,705 @@ +_frame = $frame; + $this->_min_max_child_cache = null; + $this->_min_max_cache = null; + } + + /** + * @return Dompdf + */ + function get_dompdf() + { + return $this->_frame->get_dompdf(); + } + + public function reset(): void + { + $this->_min_max_child_cache = null; + $this->_min_max_cache = null; + } + + /** + * Determine the actual containing block for absolute and fixed position. + * + * https://www.w3.org/TR/CSS21/visudet.html#containing-block-details + */ + protected function determine_absolute_containing_block(): void + { + $frame = $this->_frame; + $style = $frame->get_style(); + + switch ($style->position) { + case "absolute": + $parent = $frame->find_positioned_parent(); + if ($parent !== $frame->get_root()) { + $parent_style = $parent->get_style(); + $parent_padding_box = $parent->get_padding_box(); + //FIXME: an accurate measure of the positioned parent height + // is not possible until reflow has completed; + // we'll fall back to the parent's containing block, + // which is wrong for auto-height parents + if ($parent_style->height === "auto") { + $parent_containing_block = $parent->get_containing_block(); + $containing_block_height = $parent_containing_block["h"] - + (float)$parent_style->length_in_pt([ + $parent_style->margin_top, + $parent_style->margin_bottom, + $parent_style->border_top_width, + $parent_style->border_bottom_width + ], $parent_containing_block["w"]); + } else { + $containing_block_height = $parent_padding_box["h"]; + } + $frame->set_containing_block($parent_padding_box["x"], $parent_padding_box["y"], $parent_padding_box["w"], $containing_block_height); + break; + } + case "fixed": + $initial_cb = $frame->get_root()->get_first_child()->get_containing_block(); + $frame->set_containing_block($initial_cb["x"], $initial_cb["y"], $initial_cb["w"], $initial_cb["h"]); + break; + default: + // Nothing to do, containing block already set via parent + break; + } + } + + /** + * Collapse frames margins + * http://www.w3.org/TR/CSS21/box.html#collapsing-margins + */ + protected function _collapse_margins(): void + { + $frame = $this->_frame; + + // Margins of float/absolutely positioned/inline-level elements do not collapse + if (!$frame->is_in_flow() || $frame->is_inline_level() + || $frame->get_root() === $frame || $frame->get_parent() === $frame->get_root() + ) { + return; + } + + $cb = $frame->get_containing_block(); + $style = $frame->get_style(); + + $t = $style->length_in_pt($style->margin_top, $cb["w"]); + $b = $style->length_in_pt($style->margin_bottom, $cb["w"]); + + // Handle 'auto' values + if ($t === "auto") { + $style->set_used("margin_top", 0.0); + $t = 0.0; + } + + if ($b === "auto") { + $style->set_used("margin_bottom", 0.0); + $b = 0.0; + } + + // Collapse vertical margins: + $n = $frame->get_next_sibling(); + if ( $n && !($n->is_block_level() && $n->is_in_flow()) ) { + while ($n = $n->get_next_sibling()) { + if ($n->is_block_level() && $n->is_in_flow()) { + break; + } + + if (!$n->get_first_child()) { + $n = null; + break; + } + } + } + + if ($n) { + $n_style = $n->get_style(); + $n_t = (float)$n_style->length_in_pt($n_style->margin_top, $cb["w"]); + + $b = $this->get_collapsed_margin_length($b, $n_t); + $style->set_used("margin_bottom", $b); + $n_style->set_used("margin_top", 0.0); + } + + // Collapse our first child's margin, if there is no border or padding + if ($style->border_top_width == 0 && $style->length_in_pt($style->padding_top) == 0) { + $f = $this->_frame->get_first_child(); + if ( $f && !($f->is_block_level() && $f->is_in_flow()) ) { + while ($f = $f->get_next_sibling()) { + if ($f->is_block_level() && $f->is_in_flow()) { + break; + } + + if (!$f->get_first_child()) { + $f = null; + break; + } + } + } + + // Margins are collapsed only between block-level boxes + if ($f) { + $f_style = $f->get_style(); + $f_t = (float)$f_style->length_in_pt($f_style->margin_top, $cb["w"]); + + $t = $this->get_collapsed_margin_length($t, $f_t); + $style->set_used("margin_top", $t); + $f_style->set_used("margin_top", 0.0); + } + } + + // Collapse our last child's margin, if there is no border or padding + if ($style->border_bottom_width == 0 && $style->length_in_pt($style->padding_bottom) == 0) { + $l = $this->_frame->get_last_child(); + if ( $l && !($l->is_block_level() && $l->is_in_flow()) ) { + while ($l = $l->get_prev_sibling()) { + if ($l->is_block_level() && $l->is_in_flow()) { + break; + } + + if (!$l->get_last_child()) { + $l = null; + break; + } + } + } + + // Margins are collapsed only between block-level boxes + if ($l) { + $l_style = $l->get_style(); + $l_b = (float)$l_style->length_in_pt($l_style->margin_bottom, $cb["w"]); + + $b = $this->get_collapsed_margin_length($b, $l_b); + $style->set_used("margin_bottom", $b); + $l_style->set_used("margin_bottom", 0.0); + } + } + } + + /** + * Get the combined (collapsed) length of two adjoining margins. + * + * See http://www.w3.org/TR/CSS21/box.html#collapsing-margins. + * + * @param float $l1 + * @param float $l2 + * + * @return float + */ + private function get_collapsed_margin_length(float $l1, float $l2): float + { + if ($l1 < 0 && $l2 < 0) { + return min($l1, $l2); // min(x, y) = - max(abs(x), abs(y)), if x < 0 && y < 0 + } + + if ($l1 < 0 || $l2 < 0) { + return $l1 + $l2; // x + y = x - abs(y), if y < 0 + } + + return max($l1, $l2); + } + + /** + * Handle relative positioning according to + * https://www.w3.org/TR/CSS21/visuren.html#relative-positioning. + * + * @param AbstractFrameDecorator $frame The frame to handle. + */ + protected function position_relative(AbstractFrameDecorator $frame): void + { + $style = $frame->get_style(); + + if ($style->position === "relative") { + $cb = $frame->get_containing_block(); + $top = $style->length_in_pt($style->top, $cb["h"]); + $right = $style->length_in_pt($style->right, $cb["w"]); + $bottom = $style->length_in_pt($style->bottom, $cb["h"]); + $left = $style->length_in_pt($style->left, $cb["w"]); + + // FIXME RTL case: + // if ($left !== "auto" && $right !== "auto") $left = -$right; + if ($left === "auto" && $right === "auto") { + $left = 0; + } elseif ($left === "auto") { + $left = -$right; + } + + if ($top === "auto" && $bottom === "auto") { + $top = 0; + } elseif ($top === "auto") { + $top = -$bottom; + } + + $frame->move($left, $top); + } + } + + /** + * @param Block|null $block + */ + abstract function reflow(Block $block = null); + + /** + * Resolve the `min-width` property. + * + * Resolves to 0 if not set or if a percentage and the containing-block + * width is not defined. + * + * @param float|null $cbw Width of the containing block. + * + * @return float + */ + protected function resolve_min_width(?float $cbw): float + { + $style = $this->_frame->get_style(); + $min_width = $style->min_width; + + return $min_width !== "auto" + ? $style->length_in_pt($min_width, $cbw ?? 0) + : 0.0; + } + + /** + * Resolve the `max-width` property. + * + * Resolves to `INF` if not set or if a percentage and the containing-block + * width is not defined. + * + * @param float|null $cbw Width of the containing block. + * + * @return float + */ + protected function resolve_max_width(?float $cbw): float + { + $style = $this->_frame->get_style(); + $max_width = $style->max_width; + + return $max_width !== "none" + ? $style->length_in_pt($max_width, $cbw ?? INF) + : INF; + } + + /** + * Resolve the `min-height` property. + * + * Resolves to 0 if not set or if a percentage and the containing-block + * height is not defined. + * + * @param float|null $cbh Height of the containing block. + * + * @return float + */ + protected function resolve_min_height(?float $cbh): float + { + $style = $this->_frame->get_style(); + $min_height = $style->min_height; + + return $min_height !== "auto" + ? $style->length_in_pt($min_height, $cbh ?? 0) + : 0.0; + } + + /** + * Resolve the `max-height` property. + * + * Resolves to `INF` if not set or if a percentage and the containing-block + * height is not defined. + * + * @param float|null $cbh Height of the containing block. + * + * @return float + */ + protected function resolve_max_height(?float $cbh): float + { + $style = $this->_frame->get_style(); + $max_height = $style->max_height; + + return $max_height !== "none" + ? $style->length_in_pt($style->max_height, $cbh ?? INF) + : INF; + } + + /** + * Get the minimum and maximum preferred width of the contents of the frame, + * as requested by its children. + * + * @return array A two-element array of min and max width. + */ + public function get_min_max_child_width(): array + { + if (!is_null($this->_min_max_child_cache)) { + return $this->_min_max_child_cache; + } + + $low = []; + $high = []; + + for ($iter = $this->_frame->get_children(); $iter->valid(); $iter->next()) { + $inline_min = 0; + $inline_max = 0; + + // Add all adjacent inline widths together to calculate max width + while ($iter->valid() && ($iter->current()->is_inline_level() || $iter->current()->get_style()->display === "-dompdf-image")) { + /** @var AbstractFrameDecorator */ + $child = $iter->current(); + $child->get_reflower()->_set_content(); + $minmax = $child->get_min_max_width(); + + if (in_array($child->get_style()->white_space, ["pre", "nowrap"], true)) { + $inline_min += $minmax["min"]; + } else { + $low[] = $minmax["min"]; + } + + $inline_max += $minmax["max"]; + $iter->next(); + } + + if ($inline_min > 0) { + $low[] = $inline_min; + } + if ($inline_max > 0) { + $high[] = $inline_max; + } + + // Skip children with absolute position + if ($iter->valid() && !$iter->current()->is_absolute()) { + /** @var AbstractFrameDecorator */ + $child = $iter->current(); + $child->get_reflower()->_set_content(); + list($low[], $high[]) = $child->get_min_max_width(); + } + } + + $min = count($low) ? max($low) : 0; + $max = count($high) ? max($high) : 0; + + return $this->_min_max_child_cache = [$min, $max]; + } + + /** + * Get the minimum and maximum preferred content-box width of the frame. + * + * @return array A two-element array of min and max width. + */ + public function get_min_max_content_width(): array + { + return $this->get_min_max_child_width(); + } + + /** + * Get the minimum and maximum preferred border-box width of the frame. + * + * Required for shrink-to-fit width calculation, as used in automatic table + * layout, absolute positioning, float and inline-block. This provides a + * basic implementation. Child classes should override this or + * `get_min_max_content_width` as necessary. + * + * @return array An array `[0 => min, 1 => max, "min" => min, "max" => max]` + * of min and max width. + */ + public function get_min_max_width(): array + { + if (!is_null($this->_min_max_cache)) { + return $this->_min_max_cache; + } + + $style = $this->_frame->get_style(); + [$min, $max] = $this->get_min_max_content_width(); + + // Account for margins, borders, and padding + $dims = [ + $style->padding_left, + $style->padding_right, + $style->border_left_width, + $style->border_right_width, + $style->margin_left, + $style->margin_right + ]; + + // The containing block is not defined yet, treat percentages as 0 + $delta = (float) $style->length_in_pt($dims, 0); + $min += $delta; + $max += $delta; + + return $this->_min_max_cache = [$min, $max, "min" => $min, "max" => $max]; + } + + /** + * Parses a CSS string containing quotes and escaped hex characters + * + * @param $string string The CSS string to parse + * @param $single_trim + * @return string + */ + protected function _parse_string($string, $single_trim = false) + { + if ($single_trim) { + $string = preg_replace('/^[\"\']/', "", $string); + $string = preg_replace('/[\"\']$/', "", $string); + } else { + $string = trim($string, "'\""); + } + + $string = str_replace(["\\\n", '\\"', "\\'"], + ["", '"', "'"], $string); + + // Convert escaped hex characters into ascii characters (e.g. \A => newline) + $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", + function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); }, + $string); + return $string; + } + + /** + * Parses a CSS "quotes" property + * + * https://www.w3.org/TR/css-content-3/#quotes + * + * @return array An array of pairs of quotes + */ + protected function _parse_quotes(): array + { + $quotes = $this->_frame->get_style()->quotes; + + if ($quotes === "none") { + return []; + } + + if ($quotes === "auto") { + // TODO: Use typographically appropriate quotes for the current + // language here + return [['"', '"'], ["'", "'"]]; + } + + // Matches quote types + $re = '/(\'[^\']*\')|(\"[^\"]*\")/'; + + // Split on spaces, except within quotes + if (!preg_match_all($re, $quotes, $matches, PREG_SET_ORDER)) { + return []; + } + + $quotes_array = []; + foreach ($matches as $_quote) { + $quotes_array[] = $this->_parse_string($_quote[0], true); + } + + return array_chunk($quotes_array, 2); + } + + /** + * Parses the CSS "content" property + * + * https://www.w3.org/TR/CSS21/generate.html#content + * + * @return string The resulting string + */ + protected function _parse_content(): string + { + $style = $this->_frame->get_style(); + $content = $style->content; + + if ($content === "normal" || $content === "none") { + return ""; + } + + $quotes = $this->_parse_quotes(); + $text = ""; + + foreach ($content as $val) { + // String + if (in_array(mb_substr($val, 0, 1), ['"', "'"], true)) { + $text .= $this->_parse_string($val); + continue; + } + + $val = mb_strtolower($val); + + // Keywords + if ($val === "open-quote") { + // FIXME: Take quotation depth into account + if (isset($quotes[0][0])) { + $text .= $quotes[0][0]; + } + continue; + } elseif ($val === "close-quote") { + // FIXME: Take quotation depth into account + if (isset($quotes[0][1])) { + $text .= $quotes[0][1]; + } + continue; + } elseif ($val === "no-open-quote") { + // FIXME: Increment quotation depth + continue; + } elseif ($val === "no-close-quote") { + // FIXME: Decrement quotation depth + continue; + } + + // attr() + if (mb_substr($val, 0, 5) === "attr(") { + $i = mb_strpos($val, ")"); + if ($i === false) { + continue; + } + + $attr = trim(mb_substr($val, 5, $i - 5)); + if ($attr === "") { + continue; + } + + $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr); + continue; + } + + // counter()/counters() + if (mb_substr($val, 0, 7) === "counter") { + // Handle counter() references: + // http://www.w3.org/TR/CSS21/generate.html#content + + $i = mb_strpos($val, ")"); + if ($i === false) { + continue; + } + + preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]*)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $val, $args); + $counter_id = $args[3]; + + if (strtolower($args[1]) === "counter") { + // counter(name [,style]) + if (isset($args[5])) { + $type = trim($args[5]); + } else { + $type = "decimal"; + } + $p = $this->_frame->lookup_counter_frame($counter_id); + + $text .= $p->counter_value($counter_id, $type); + } elseif (strtolower($args[1]) === "counters") { + // counters(name, string [,style]) + if (isset($args[5])) { + $string = $this->_parse_string($args[5]); + } else { + $string = ""; + } + + if (isset($args[7])) { + $type = trim($args[7]); + } else { + $type = "decimal"; + } + + $p = $this->_frame->lookup_counter_frame($counter_id); + $tmp = []; + while ($p) { + // We only want to use the counter values when they actually increment the counter + if (array_key_exists($counter_id, $p->_counters)) { + array_unshift($tmp, $p->counter_value($counter_id, $type)); + } + $p = $p->lookup_counter_frame($counter_id); + } + $text .= implode($string, $tmp); + } else { + // countertops? + } + + continue; + } + } + + return $text; + } + + /** + * Handle counters and set generated content if the frame is a + * generated-content frame. + */ + protected function _set_content(): void + { + $frame = $this->_frame; + + if ($frame->content_set) { + return; + } + + $style = $frame->get_style(); + + if (($reset = $style->counter_reset) !== "none") { + $frame->reset_counters($reset); + } + + if (($increment = $style->counter_increment) !== "none") { + $frame->increment_counters($increment); + } + + if ($frame->get_node()->nodeName === "dompdf_generated") { + $content = $this->_parse_content(); + + if ($content !== "") { + $node = $frame->get_node()->ownerDocument->createTextNode($content); + + $new_style = $style->get_stylesheet()->create_style(); + $new_style->inherit($style); + + $new_frame = new Frame($node); + $new_frame->set_style($new_style); + + Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root()); + $frame->append_child($new_frame); + } + } + + $frame->content_set = true; + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Block.php new file mode 100644 index 000000000..f9e50a678 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Block.php @@ -0,0 +1,949 @@ +_frame; + $style = $frame->get_style(); + $absolute = $frame->is_absolute(); + + $cb = $frame->get_containing_block(); + $w = $cb["w"]; + + $rm = $style->length_in_pt($style->margin_right, $w); + $lm = $style->length_in_pt($style->margin_left, $w); + + $left = $style->length_in_pt($style->left, $w); + $right = $style->length_in_pt($style->right, $w); + + // Handle 'auto' values + $dims = [$style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right, + $width !== "auto" ? $width : 0, + $rm !== "auto" ? $rm : 0, + $lm !== "auto" ? $lm : 0]; + + // absolutely positioned boxes take the 'left' and 'right' properties into account + if ($absolute) { + $dims[] = $left !== "auto" ? $left : 0; + $dims[] = $right !== "auto" ? $right : 0; + } + + $sum = (float)$style->length_in_pt($dims, $w); + + // Compare to the containing block + $diff = $w - $sum; + + if ($absolute) { + // Absolutely positioned + // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width + + if ($width === "auto" || $left === "auto" || $right === "auto") { + // "all of the three are 'auto'" logic + otherwise case + if ($lm === "auto") { + $lm = 0; + } + if ($rm === "auto") { + $rm = 0; + } + + $block_parent = $frame->find_block_parent(); + $parent_content = $block_parent->get_content_box(); + $line = $block_parent->get_current_line_box(); + + // TODO: This is the in-flow inline position. Use the in-flow + // block position if the original display type is block-level + $inflow_x = $parent_content["x"] - $cb["x"] + $line->left + $line->w; + + if ($width === "auto" && $left === "auto" && $right === "auto") { + // rule 3, per instruction preceding rule set + // shrink-to-fit width + $left = $inflow_x; + [$min, $max] = $this->get_min_max_child_width(); + $width = min(max($min, $diff - $left), $max); + $right = $diff - $left - $width; + } elseif ($width === "auto" && $left === "auto") { + // rule 1 + // shrink-to-fit width + [$min, $max] = $this->get_min_max_child_width(); + $width = min(max($min, $diff), $max); + $left = $diff - $width; + } elseif ($width === "auto" && $right === "auto") { + // rule 3 + // shrink-to-fit width + [$min, $max] = $this->get_min_max_child_width(); + $width = min(max($min, $diff), $max); + $right = $diff - $width; + } elseif ($left === "auto" && $right === "auto") { + // rule 2 + $left = $inflow_x; + $right = $diff - $left; + } elseif ($left === "auto") { + // rule 4 + $left = $diff; + } elseif ($width === "auto") { + // rule 5 + $width = max($diff, 0); + } else { + // $right === "auto" + // rule 6 + $right = $diff; + } + } else { + // "none of the three are 'auto'" logic described in paragraph preceding the rules + if ($diff >= 0) { + if ($lm === "auto" && $rm === "auto") { + $lm = $rm = $diff / 2; + } elseif ($lm === "auto") { + $lm = $diff; + } elseif ($rm === "auto") { + $rm = $diff; + } + } else { + // over-constrained, solve for right + $right = $right + $diff; + + if ($lm === "auto") { + $lm = 0; + } + if ($rm === "auto") { + $rm = 0; + } + } + } + } elseif ($style->float !== "none" || $style->display === "inline-block") { + // Shrink-to-fit width for float and inline block + // https://www.w3.org/TR/CSS21/visudet.html#float-width + // https://www.w3.org/TR/CSS21/visudet.html#inlineblock-width + + if ($width === "auto") { + [$min, $max] = $this->get_min_max_child_width(); + $width = min(max($min, $diff), $max); + } + if ($lm === "auto") { + $lm = 0; + } + if ($rm === "auto") { + $rm = 0; + } + } else { + // Block-level, normal flow + // https://www.w3.org/TR/CSS21/visudet.html#blockwidth + + if ($diff >= 0) { + // Find auto properties and get them to take up the slack + if ($width === "auto") { + $width = $diff; + + if ($lm === "auto") { + $lm = 0; + } + if ($rm === "auto") { + $rm = 0; + } + } elseif ($lm === "auto" && $rm === "auto") { + $lm = $rm = $diff / 2; + } elseif ($lm === "auto") { + $lm = $diff; + } elseif ($rm === "auto") { + $rm = $diff; + } + } else { + // We are over constrained--set margin-right to the difference + $rm = (float) $rm + $diff; + + if ($width === "auto") { + $width = 0; + } + if ($lm === "auto") { + $lm = 0; + } + } + } + + return [ + "width" => $width, + "margin_left" => $lm, + "margin_right" => $rm, + "left" => $left, + "right" => $right, + ]; + } + + /** + * Call the above function, but resolve max/min widths + * + * @throws Exception + * @return array + */ + protected function _calculate_restricted_width() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + + if (!isset($cb["w"])) { + throw new Exception("Box property calculation requires containing block width"); + } + + $width = $style->length_in_pt($style->width, $cb["w"]); + + $values = $this->_calculate_width($width); + $margin_left = $values["margin_left"]; + $margin_right = $values["margin_right"]; + $width = $values["width"]; + $left = $values["left"]; + $right = $values["right"]; + + // Handle min/max width + // https://www.w3.org/TR/CSS21/visudet.html#min-max-widths + $min_width = $this->resolve_min_width($cb["w"]); + $max_width = $this->resolve_max_width($cb["w"]); + + if ($width > $max_width) { + $values = $this->_calculate_width($max_width); + $margin_left = $values["margin_left"]; + $margin_right = $values["margin_right"]; + $width = $values["width"]; + $left = $values["left"]; + $right = $values["right"]; + } + + if ($width < $min_width) { + $values = $this->_calculate_width($min_width); + $margin_left = $values["margin_left"]; + $margin_right = $values["margin_right"]; + $width = $values["width"]; + $left = $values["left"]; + $right = $values["right"]; + } + + return [$width, $margin_left, $margin_right, $left, $right]; + } + + /** + * Determine the unrestricted height of content within the block + * not by adding each line's height, but by getting the last line's position. + * This because lines could have been pushed lower by a clearing element. + * + * @return float + */ + protected function _calculate_content_height() + { + $height = 0; + $lines = $this->_frame->get_line_boxes(); + if (count($lines) > 0) { + $last_line = end($lines); + $content_box = $this->_frame->get_content_box(); + $height = $last_line->y + $last_line->h - $content_box["y"]; + } + return $height; + } + + /** + * Determine the frame's restricted height + * + * @return array + */ + protected function _calculate_restricted_height() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $content_height = $this->_calculate_content_height(); + $cb = $frame->get_containing_block(); + + $height = $style->length_in_pt($style->height, $cb["h"]); + $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]); + $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]); + + $top = $style->length_in_pt($style->top, $cb["h"]); + $bottom = $style->length_in_pt($style->bottom, $cb["h"]); + + if ($frame->is_absolute()) { + // Absolutely positioned + // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height + + $h_dims = [ + $top !== "auto" ? $top : 0, + $height !== "auto" ? $height : 0, + $bottom !== "auto" ? $bottom : 0 + ]; + $w_dims = [ + $style->margin_top !== "auto" ? $style->margin_top : 0, + $style->padding_top, + $style->border_top_width, + $style->border_bottom_width, + $style->padding_bottom, + $style->margin_bottom !== "auto" ? $style->margin_bottom : 0 + ]; + + $sum = (float)$style->length_in_pt($h_dims, $cb["h"]) + + (float)$style->length_in_pt($w_dims, $cb["w"]); + + $diff = $cb["h"] - $sum; + + if ($height === "auto" || $top === "auto" || $bottom === "auto") { + // "all of the three are 'auto'" logic + otherwise case + if ($margin_top === "auto") { + $margin_top = 0; + } + if ($margin_bottom === "auto") { + $margin_bottom = 0; + } + + $block_parent = $frame->find_block_parent(); + $current_line = $block_parent->get_current_line_box(); + + // TODO: This is the in-flow inline position. Use the in-flow + // block position if the original display type is block-level + $inflow_y = $current_line->y - $cb["y"]; + + if ($height === "auto" && $top === "auto" && $bottom === "auto") { + // rule 3, per instruction preceding rule set + $top = $inflow_y; + $height = $content_height; + $bottom = $diff - $top - $height; + } elseif ($height === "auto" && $top === "auto") { + // rule 1 + $height = $content_height; + $top = $diff - $height; + } elseif ($height === "auto" && $bottom === "auto") { + // rule 3 + $height = $content_height; + $bottom = $diff - $height; + } elseif ($top === "auto" && $bottom === "auto") { + // rule 2 + $top = $inflow_y; + $bottom = $diff - $top; + } elseif ($top === "auto") { + // rule 4 + $top = $diff; + } elseif ($height === "auto") { + // rule 5 + $height = max($diff, 0); + } else { + // $bottom === "auto" + // rule 6 + $bottom = $diff; + } + } else { + // "none of the three are 'auto'" logic described in paragraph preceding the rules + if ($diff >= 0) { + if ($margin_top === "auto" && $margin_bottom === "auto") { + $margin_top = $margin_bottom = $diff / 2; + } elseif ($margin_top === "auto") { + $margin_top = $diff; + } elseif ($margin_bottom === "auto") { + $margin_bottom = $diff; + } + } else { + // over-constrained, solve for bottom + $bottom = $bottom + $diff; + + if ($margin_top === "auto") { + $margin_top = 0; + } + if ($margin_bottom === "auto") { + $margin_bottom = 0; + } + } + } + } else { + // https://www.w3.org/TR/CSS21/visudet.html#normal-block + // https://www.w3.org/TR/CSS21/visudet.html#block-root-margin + + if ($height === "auto") { + $height = $content_height; + } + if ($margin_top === "auto") { + $margin_top = 0; + } + if ($margin_bottom === "auto") { + $margin_bottom = 0; + } + + // Handle min/max height + // https://www.w3.org/TR/CSS21/visudet.html#min-max-heights + $min_height = $this->resolve_min_height($cb["h"]); + $max_height = $this->resolve_max_height($cb["h"]); + $height = Helpers::clamp($height, $min_height, $max_height); + } + + // TODO: Need to also take min/max height into account for absolute + // positioning, using similar logic to the `_calculate_width`/ + // `calculate_restricted_width` split above. The non-absolute case + // can simply clamp height within min/max, as margins and offsets are + // not affected + + return [$height, $margin_top, $margin_bottom, $top, $bottom]; + } + + /** + * Adjust the justification of each of our lines. + * http://www.w3.org/TR/CSS21/text.html#propdef-text-align + */ + protected function _text_align() + { + $style = $this->_frame->get_style(); + $w = $this->_frame->get_containing_block("w"); + $width = (float)$style->length_in_pt($style->width, $w); + $text_indent = (float)$style->length_in_pt($style->text_indent, $w); + + switch ($style->text_align) { + default: + case "left": + foreach ($this->_frame->get_line_boxes() as $line) { + if (!$line->inline) { + continue; + } + + $line->trim_trailing_ws(); + + if ($line->left) { + foreach ($line->frames_to_align() as $frame) { + $frame->move($line->left, 0); + } + } + } + break; + + case "right": + foreach ($this->_frame->get_line_boxes() as $i => $line) { + if (!$line->inline) { + continue; + } + + $line->trim_trailing_ws(); + + $indent = $i === 0 ? $text_indent : 0; + $dx = $width - $line->w - $line->right - $indent; + + foreach ($line->frames_to_align() as $frame) { + $frame->move($dx, 0); + } + } + break; + + case "justify": + // We justify all lines except the last one, unless the frame + // has been split, in which case the actual last line is part of + // the split-off frame + $lines = $this->_frame->get_line_boxes(); + $last_line_index = $this->_frame->is_split ? null : count($lines) - 1; + + foreach ($lines as $i => $line) { + if (!$line->inline) { + continue; + } + + $line->trim_trailing_ws(); + + if ($line->left) { + foreach ($line->frames_to_align() as $frame) { + $frame->move($line->left, 0); + } + } + + if ($line->br || $i === $last_line_index) { + continue; + } + + $frames = $line->get_frames(); + $other_frame_count = 0; + + foreach ($frames as $frame) { + if (!($frame instanceof TextFrameDecorator)) { + $other_frame_count++; + } + } + + $word_count = $line->wc + $other_frame_count; + + // Set the spacing for each child + if ($word_count > 1) { + $indent = $i === 0 ? $text_indent : 0; + $spacing = ($width - $line->get_width() - $indent) / ($word_count - 1); + } else { + $spacing = 0; + } + + $dx = 0; + foreach ($frames as $frame) { + if ($frame instanceof TextFrameDecorator) { + $text = $frame->get_text(); + $spaces = mb_substr_count($text, " "); + + $frame->move($dx, 0); + $frame->set_text_spacing($spacing); + + $dx += $spaces * $spacing; + } else { + $frame->move($dx, 0); + } + } + + // The line (should) now occupy the entire width + $line->w = $width; + } + break; + + case "center": + case "centre": + foreach ($this->_frame->get_line_boxes() as $i => $line) { + if (!$line->inline) { + continue; + } + + $line->trim_trailing_ws(); + + $indent = $i === 0 ? $text_indent : 0; + $dx = ($width + $line->left - $line->w - $line->right - $indent) / 2; + + foreach ($line->frames_to_align() as $frame) { + $frame->move($dx, 0); + } + } + break; + } + } + + /** + * Align inline children vertically. + * Aligns each child vertically after each line is reflowed + */ + function vertical_align() + { + $fontMetrics = $this->get_dompdf()->getFontMetrics(); + + foreach ($this->_frame->get_line_boxes() as $line) { + $height = $line->h; + + // Move all markers to the top of the line box + foreach ($line->get_list_markers() as $marker) { + $x = $marker->get_position("x"); + $marker->set_position($x, $line->y); + } + + foreach ($line->frames_to_align() as $frame) { + $style = $frame->get_style(); + $isInlineBlock = $style->display !== "inline" + && $style->display !== "-dompdf-list-bullet"; + + $baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size); + $y_offset = 0; + + //FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?) + if ($isInlineBlock) { + // Workaround: Skip vertical alignment if the frame is the + // only one one the line, excluding empty text frames, which + // may be the result of trailing white space + // FIXME: This special case should be removed once vertical + // alignment is properly fixed + $skip = true; + + foreach ($line->get_frames() as $other) { + if ($other !== $frame + && !($other->is_text_node() && $other->get_node()->nodeValue === "") + ) { + $skip = false; + break; + } + } + + if ($skip) { + continue; + } + + $marginHeight = $frame->get_margin_height(); + $imageHeightDiff = $height * 0.8 - $marginHeight; + + $align = $frame->get_style()->vertical_align; + if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) { + switch ($align) { + case "middle": + $y_offset = $imageHeightDiff / 2; + break; + + case "sub": + $y_offset = 0.3 * $height + $imageHeightDiff; + break; + + case "super": + $y_offset = -0.2 * $height + $imageHeightDiff; + break; + + case "text-top": // FIXME: this should be the height of the frame minus the height of the text + $y_offset = $height - $style->line_height; + break; + + case "top": + break; + + case "text-bottom": // FIXME: align bottom of image with the descender? + case "bottom": + $y_offset = 0.3 * $height + $imageHeightDiff; + break; + + case "baseline": + default: + $y_offset = $imageHeightDiff; + break; + } + } else { + $y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - $marginHeight; + } + } else { + $parent = $frame->get_parent(); + if ($parent instanceof TableCellFrameDecorator) { + $align = "baseline"; + } else { + $align = $parent->get_style()->vertical_align; + } + if (in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, true)) { + switch ($align) { + case "middle": + $y_offset = ($height * 0.8 - $baseline) / 2; + break; + + case "sub": + $y_offset = $height * 0.8 - $baseline * 0.5; + break; + + case "super": + $y_offset = $height * 0.8 - $baseline * 1.4; + break; + + case "text-top": + case "top": // Not strictly accurate, but good enough for now + break; + + case "text-bottom": + case "bottom": + $y_offset = $height * 0.8 - $baseline; + break; + + case "baseline": + default: + $y_offset = $height * 0.8 - $baseline; + break; + } + } else { + $y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size); + } + } + + if ($y_offset !== 0) { + $frame->move(0, $y_offset); + } + } + } + } + + /** + * @param AbstractFrameDecorator $child + */ + function process_clear(AbstractFrameDecorator $child) + { + $child_style = $child->get_style(); + $root = $this->_frame->get_root(); + + // Handle "clear" + if ($child_style->clear !== "none") { + //TODO: this is a WIP for handling clear/float frames that are in between inline frames + if ($child->get_prev_sibling() !== null) { + $this->_frame->add_line(); + } + if ($child_style->float !== "none" && $child->get_next_sibling()) { + $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1); + } + + $lowest_y = $root->get_lowest_float_offset($child); + + // If a float is still applying, we handle it + if ($lowest_y) { + if ($child->is_in_flow()) { + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $lowest_y + $child->get_margin_height(); + $line_box->left = 0; + $line_box->right = 0; + } + + $child->move(0, $lowest_y - $child->get_position("y")); + } + } + } + + /** + * @param AbstractFrameDecorator $child + * @param float $cb_x + * @param float $cb_w + */ + function process_float(AbstractFrameDecorator $child, $cb_x, $cb_w) + { + $child_style = $child->get_style(); + $root = $this->_frame->get_root(); + + // Handle "float" + if ($child_style->float !== "none") { + $root->add_floating_frame($child); + + // Remove next frame's beginning whitespace + $next = $child->get_next_sibling(); + if ($next && $next instanceof TextFrameDecorator) { + $next->set_text(ltrim($next->get_text())); + } + + $line_box = $this->_frame->get_current_line_box(); + list($old_x, $old_y) = $child->get_position(); + + $float_x = $cb_x; + $float_y = $old_y; + $float_w = $child->get_margin_width(); + + if ($child_style->clear === "none") { + switch ($child_style->float) { + case "left": + $float_x += $line_box->left; + break; + case "right": + $float_x += ($cb_w - $line_box->right - $float_w); + break; + } + } else { + if ($child_style->float === "right") { + $float_x += ($cb_w - $float_w); + } + } + + if ($cb_w < $float_x + $float_w - $old_x) { + // TODO handle when floating elements don't fit + } + + $line_box->get_float_offsets(); + + if ($child->_float_next_line) { + $float_y += $line_box->h; + } + + $child->set_position($float_x, $float_y); + $child->move($float_x - $old_x, $float_y - $old_y, true); + } + } + + /** + * @param BlockFrameDecorator $block + */ + function reflow(BlockFrameDecorator $block = null) + { + + // Check if a page break is forced + $page = $this->_frame->get_root(); + $page->check_forced_page_break($this->_frame); + + // Bail if the page is full + if ($page->is_full()) { + return; + } + + $this->determine_absolute_containing_block(); + + // Counters and generated content + $this->_set_content(); + + // Inherit any dangling list markers + if ($block && $this->_frame->is_in_flow()) { + $this->_frame->inherit_dangling_markers($block); + } + + // Collapse margins if required + $this->_collapse_margins(); + + $style = $this->_frame->get_style(); + $cb = $this->_frame->get_containing_block(); + + // Determine the constraints imposed by this frame: calculate the width + // of the content area: + [$width, $margin_left, $margin_right, $left, $right] = $this->_calculate_restricted_width(); + + // Store the calculated properties + $style->set_used("width", $width); + $style->set_used("margin_left", $margin_left); + $style->set_used("margin_right", $margin_right); + $style->set_used("left", $left); + $style->set_used("right", $right); + + $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]); + $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]); + + $auto_top = $style->top === "auto"; + $auto_margin_top = $margin_top === "auto"; + + // Update the position + $this->_frame->position(); + [$x, $y] = $this->_frame->get_position(); + + // Adjust the first line based on the text-indent property + $indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]); + $this->_frame->increase_line_width($indent); + + // Determine the content edge + $top = (float)$style->length_in_pt([ + $margin_top !== "auto" ? $margin_top : 0, + $style->border_top_width, + $style->padding_top + ], $cb["w"]); + $bottom = (float)$style->length_in_pt([ + $margin_bottom !== "auto" ? $margin_bottom : 0, + $style->border_bottom_width, + $style->padding_bottom + ], $cb["w"]); + + $cb_x = $x + (float)$margin_left + (float)$style->length_in_pt([$style->border_left_width, + $style->padding_left], $cb["w"]); + + $cb_y = $y + $top; + + $height = $style->length_in_pt($style->height, $cb["h"]); + if ($height === "auto") { + $height = ($cb["h"] + $cb["y"]) - $bottom - $cb_y; + } + + // Set the y position of the first line in this block + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $cb_y; + $line_box->get_float_offsets(); + + // Set the containing blocks and reflow each child + foreach ($this->_frame->get_children() as $child) { + $child->set_containing_block($cb_x, $cb_y, $width, $height); + $this->process_clear($child); + $child->reflow($this->_frame); + + // Check for a page break before the child + $page->check_page_break($child); + + // Don't add the child to the line if a page break has occurred + // before it (possibly via a descendant), in which case it has been + // reset, including its position + if ($page->is_full() && $child->get_position("x") === null) { + break; + } + + $this->process_float($child, $cb_x, $width); + } + + // Stop reflow if a page break has occurred before the frame, in which + // case it has been reset, including its position + if ($page->is_full() && $this->_frame->get_position("x") === null) { + return; + } + + // Determine our height + [$height, $margin_top, $margin_bottom, $top, $bottom] = $this->_calculate_restricted_height(); + + $style->set_used("height", $height); + $style->set_used("margin_top", $margin_top); + $style->set_used("margin_bottom", $margin_bottom); + $style->set_used("top", $top); + $style->set_used("bottom", $bottom); + + if ($this->_frame->is_absolute()) { + if ($auto_top) { + $this->_frame->move(0, $top); + } + if ($auto_margin_top) { + $this->_frame->move(0, $margin_top, true); + } + } + + $this->_text_align(); + $this->vertical_align(); + + // Handle relative positioning + foreach ($this->_frame->get_children() as $child) { + $this->position_relative($child); + } + + if ($block && $this->_frame->is_in_flow()) { + $block->add_frame_to_line($this->_frame); + + if ($this->_frame->is_block_level()) { + $block->add_line(); + } + } + } + + public function get_min_max_content_width(): array + { + // TODO: While the containing block is not set yet on the frame, it can + // already be determined in some cases due to fixed dimensions on the + // ancestor forming the containing block. In such cases, percentage + // values could be resolved here + $style = $this->_frame->get_style(); + $width = $style->width; + $fixed_width = $width !== "auto" && !Helpers::is_percent($width); + + // If the frame has a specified width, then we don't need to check + // its children + if ($fixed_width) { + $min = (float) $style->length_in_pt($width, 0); + $max = $min; + } else { + [$min, $max] = $this->get_min_max_child_width(); + } + + // Handle min/max width style properties + $min_width = $this->resolve_min_width(null); + $max_width = $this->resolve_max_width(null); + $min = Helpers::clamp($min, $min_width, $max_width); + $max = Helpers::clamp($max, $min_width, $max_width); + + return [$min, $max]; + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Image.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Image.php new file mode 100644 index 000000000..eae34087b --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Image.php @@ -0,0 +1,213 @@ +determine_absolute_containing_block(); + + // Counters and generated content + $this->_set_content(); + + //FLOAT + //$frame = $this->_frame; + //$page = $frame->get_root(); + + //if ($frame->get_style()->float !== "none" ) { + // $page->add_floating_frame($this); + //} + + $this->resolve_dimensions(); + $this->resolve_margins(); + + $frame = $this->_frame; + $frame->position(); + + if ($block && $frame->is_in_flow()) { + $block->add_frame_to_line($frame); + } + } + + public function get_min_max_content_width(): array + { + // TODO: While the containing block is not set yet on the frame, it can + // already be determined in some cases due to fixed dimensions on the + // ancestor forming the containing block. In such cases, percentage + // values could be resolved here + $style = $this->_frame->get_style(); + + [$width] = $this->calculate_size(null, null); + $min_width = $this->resolve_min_width(null); + $percent_width = Helpers::is_percent($style->width) + || Helpers::is_percent($style->max_width) + || ($style->width === "auto" + && (Helpers::is_percent($style->height) || Helpers::is_percent($style->max_height))); + + // Use the specified min width as minimum when width or max width depend + // on the containing block and cannot be resolved yet. This mimics + // browser behavior + $min = $percent_width ? $min_width : $width; + $max = $width; + + return [$min, $max]; + } + + /** + * Calculate width and height, accounting for min/max constraints. + * + * * https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width + * * https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height + * * https://www.w3.org/TR/CSS21/visudet.html#min-max-widths + * * https://www.w3.org/TR/CSS21/visudet.html#min-max-heights + * + * @param float|null $cbw Width of the containing block. + * @param float|null $cbh Height of the containing block. + * + * @return float[] + */ + protected function calculate_size(?float $cbw, ?float $cbh): array + { + /** @var ImageFrameDecorator */ + $frame = $this->_frame; + $style = $frame->get_style(); + + $computed_width = $style->width; + $computed_height = $style->height; + + $width = $cbw === null && Helpers::is_percent($computed_width) + ? "auto" + : $style->length_in_pt($computed_width, $cbw ?? 0); + $height = $cbh === null && Helpers::is_percent($computed_height) + ? "auto" + : $style->length_in_pt($computed_height, $cbh ?? 0); + $min_width = $this->resolve_min_width($cbw); + $max_width = $this->resolve_max_width($cbw); + $min_height = $this->resolve_min_height($cbh); + $max_height = $this->resolve_max_height($cbh); + + if ($width === "auto" && $height === "auto") { + // Use intrinsic dimensions, resampled to pt + [$img_width, $img_height] = $frame->get_intrinsic_dimensions(); + $w = $frame->resample($img_width); + $h = $frame->resample($img_height); + + // Resolve min/max constraints according to the constraint-violation + // table in https://www.w3.org/TR/CSS21/visudet.html#min-max-widths + $max_width = max($min_width, $max_width); + $max_height = max($min_height, $max_height); + + if (($w > $max_width && $h <= $max_height) + || ($w > $max_width && $h > $max_height && $max_width / $w <= $max_height / $h) + || ($w < $min_width && $h > $min_height) + || ($w < $min_width && $h < $min_height && $min_width / $w > $min_height / $h) + ) { + $width = Helpers::clamp($w, $min_width, $max_width); + $height = $width * ($img_height / $img_width); + $height = Helpers::clamp($height, $min_height, $max_height); + } else { + $height = Helpers::clamp($h, $min_height, $max_height); + $width = $height * ($img_width / $img_height); + $width = Helpers::clamp($width, $min_width, $max_width); + } + } elseif ($height === "auto") { + // Width is fixed, scale height according to aspect ratio + [$img_width, $img_height] = $frame->get_intrinsic_dimensions(); + $width = Helpers::clamp((float) $width, $min_width, $max_width); + $height = $width * ($img_height / $img_width); + $height = Helpers::clamp($height, $min_height, $max_height); + } elseif ($width === "auto") { + // Height is fixed, scale width according to aspect ratio + [$img_width, $img_height] = $frame->get_intrinsic_dimensions(); + $height = Helpers::clamp((float) $height, $min_height, $max_height); + $width = $height * ($img_width / $img_height); + $width = Helpers::clamp($width, $min_width, $max_width); + } else { + // Width and height are fixed + $width = Helpers::clamp((float) $width, $min_width, $max_width); + $height = Helpers::clamp((float) $height, $min_height, $max_height); + } + + return [$width, $height]; + } + + protected function resolve_dimensions(): void + { + /** @var ImageFrameDecorator */ + $frame = $this->_frame; + $style = $frame->get_style(); + + $debug_png = $this->get_dompdf()->getOptions()->getDebugPng(); + + if ($debug_png) { + [$img_width, $img_height] = $frame->get_intrinsic_dimensions(); + print "resolve_dimensions() " . + $frame->get_style()->width . " " . + $frame->get_style()->height . ";" . + $frame->get_parent()->get_style()->width . " " . + $frame->get_parent()->get_style()->height . ";" . + $frame->get_parent()->get_parent()->get_style()->width . " " . + $frame->get_parent()->get_parent()->get_style()->height . ";" . + $img_width . " " . + $img_height . "|"; + } + + [, , $cbw, $cbh] = $frame->get_containing_block(); + [$width, $height] = $this->calculate_size($cbw, $cbh); + + if ($debug_png) { + print $width . " " . $height . ";"; + } + + $style->set_used("width", $width); + $style->set_used("height", $height); + } + + protected function resolve_margins(): void + { + // Only handle the inline case for now + // https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width + // https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height + $style = $this->_frame->get_style(); + + if ($style->margin_left === "auto") { + $style->set_used("margin_left", 0.0); + } + if ($style->margin_right === "auto") { + $style->set_used("margin_right", 0.0); + } + if ($style->margin_top === "auto") { + $style->set_used("margin_top", 0.0); + } + if ($style->margin_bottom === "auto") { + $style->set_used("margin_bottom", 0.0); + } + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Inline.php new file mode 100644 index 000000000..34a66860d --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Inline.php @@ -0,0 +1,188 @@ +_frame; + $style = $frame->get_style(); + + // Resolve width, so the margin width can be checked + $style->set_used("width", 0.0); + + $cb = $frame->get_containing_block(); + $line = $block->get_current_line_box(); + $width = $frame->get_margin_width(); + + if ($width > ($cb["w"] - $line->left - $line->w - $line->right)) { + $block->add_line(); + + // Find the appropriate inline ancestor to split + $child = $frame; + $p = $child->get_parent(); + while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) { + $child = $p; + $p = $p->get_parent(); + } + + if ($p instanceof InlineFrameDecorator) { + // Split parent and stop current reflow. Reflow continues + // via child-reflow loop of split parent + $p->split($child); + return; + } + } + + $frame->position(); + $block->add_frame_to_line($frame); + } + + /** + * @param BlockFrameDecorator|null $block + */ + function reflow(BlockFrameDecorator $block = null) + { + /** @var InlineFrameDecorator */ + $frame = $this->_frame; + + // Check if a page break is forced + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + if ($page->is_full()) { + return; + } + + // Counters and generated content + $this->_set_content(); + + $style = $frame->get_style(); + + // Resolve auto margins + // https://www.w3.org/TR/CSS21/visudet.html#inline-width + // https://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced + if ($style->margin_left === "auto") { + $style->set_used("margin_left", 0.0); + } + if ($style->margin_right === "auto") { + $style->set_used("margin_right", 0.0); + } + if ($style->margin_top === "auto") { + $style->set_used("margin_top", 0.0); + } + if ($style->margin_bottom === "auto") { + $style->set_used("margin_bottom", 0.0); + } + + // Handle line breaks + if ($frame->get_node()->nodeName === "br") { + if ($block) { + $line = $block->get_current_line_box(); + $frame->set_containing_line($line); + $block->maximize_line_height($frame->get_margin_height(), $frame); + $block->add_line(true); + + $next = $frame->get_next_sibling(); + $p = $frame->get_parent(); + + if ($next && $p instanceof InlineFrameDecorator) { + $p->split($next); + } + } + return; + } + + // Handle empty inline frames + if (!$frame->get_first_child()) { + if ($block) { + $this->reflow_empty($block); + } + return; + } + + // Add our margin, padding & border to the first and last children + if (($f = $frame->get_first_child()) && $f instanceof TextFrameDecorator) { + $f_style = $f->get_style(); + $f_style->margin_left = $style->margin_left; + $f_style->padding_left = $style->padding_left; + $f_style->border_left_width = $style->border_left_width; + $f_style->border_left_style = $style->border_left_style; + $f_style->border_left_color = $style->border_left_color; + } + + if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) { + $l_style = $l->get_style(); + $l_style->margin_right = $style->margin_right; + $l_style->padding_right = $style->padding_right; + $l_style->border_right_width = $style->border_right_width; + $l_style->border_right_style = $style->border_right_style; + $l_style->border_right_color = $style->border_right_color; + } + + $cb = $frame->get_containing_block(); + + // Set the containing blocks and reflow each child. The containing + // block is not changed by line boxes. + foreach ($frame->get_children() as $child) { + $child->set_containing_block($cb); + $child->reflow($block); + + // Stop reflow if the frame has been reset by a line or page break + // due to child reflow + if (!$frame->content_set) { + return; + } + } + + if (!$frame->get_first_child()) { + return; + } + + // Assume the position of the first child + [$x, $y] = $frame->get_first_child()->get_position(); + $frame->set_position($x, $y); + + // Handle relative positioning + foreach ($frame->get_children() as $child) { + $this->position_relative($child); + } + + if ($block) { + $block->add_frame_to_line($frame); + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php similarity index 53% rename from library/vendor/dompdf/src/FrameReflower/ListBullet.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php index 48613ccff..f735bae16 100644 --- a/library/vendor/dompdf/src/FrameReflower/ListBullet.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/ListBullet.php @@ -1,14 +1,13 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameReflower; use Dompdf\FrameDecorator\Block as BlockFrameDecorator; -use Dompdf\FrameDecorator\AbstractFrameDecorator; +use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator; /** * Reflows list bullets @@ -20,9 +19,9 @@ class ListBullet extends AbstractFrameReflower /** * ListBullet constructor. - * @param AbstractFrameDecorator $frame + * @param ListBulletFrameDecorator $frame */ - function __construct(AbstractFrameDecorator $frame) + function __construct(ListBulletFrameDecorator $frame) { parent::__construct($frame); } @@ -32,14 +31,17 @@ class ListBullet extends AbstractFrameReflower */ function reflow(BlockFrameDecorator $block = null) { - $style = $this->_frame->get_style(); + /** @var ListBulletFrameDecorator */ + $frame = $this->_frame; + $style = $frame->get_style(); - $style->width = $this->_frame->get_width(); - $this->_frame->position(); + $style->set_used("width", $frame->get_width()); + $frame->position(); if ($style->list_style_position === "inside") { - $p = $this->_frame->find_block_parent(); - $p->add_frame_to_line($this->_frame); + $block->add_frame_to_line($frame); + } else { + $block->add_dangling_marker($frame); } } } diff --git a/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/NullFrameReflower.php similarity index 87% rename from library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/NullFrameReflower.php index 8bdb0f1ad..8d7e5583c 100644 --- a/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/NullFrameReflower.php @@ -1,11 +1,9 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\FrameReflower; use Dompdf\Frame; diff --git a/library/vendor/dompdf/src/FrameReflower/Page.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Page.php similarity index 76% rename from library/vendor/dompdf/src/FrameReflower/Page.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Page.php index dc87705c1..923865b53 100644 --- a/library/vendor/dompdf/src/FrameReflower/Page.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Page.php @@ -1,9 +1,7 @@ - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameReflower; @@ -44,8 +42,8 @@ class Page extends AbstractFrameReflower } /** - * @param Frame $frame - * @param $page_number + * @param PageFrameDecorator $frame + * @param int $page_number */ function apply_page_style(Frame $frame, $page_number) { @@ -83,6 +81,8 @@ class Page extends AbstractFrameReflower $frame->set_style($style); } + + $frame->calculate_bottom_page_edge(); } /** @@ -93,18 +93,20 @@ class Page extends AbstractFrameReflower */ function reflow(BlockFrameDecorator $block = null) { - $fixed_children = array(); + /** @var PageFrameDecorator $frame */ + $frame = $this->_frame; + $child = $frame->get_first_child(); + $fixed_children = []; $prev_child = null; - $child = $this->_frame->get_first_child(); $current_page = 0; while ($child) { - $this->apply_page_style($this->_frame, $current_page + 1); + $this->apply_page_style($frame, $current_page + 1); - $style = $this->_frame->get_style(); + $style = $frame->get_style(); // Pages are only concerned with margins - $cb = $this->_frame->get_containing_block(); + $cb = $frame->get_containing_block(); $left = (float)$style->length_in_pt($style->margin_left, $cb["w"]); $right = (float)$style->length_in_pt($style->margin_right, $cb["w"]); $top = (float)$style->length_in_pt($style->margin_top, $cb["h"]); @@ -117,8 +119,7 @@ class Page extends AbstractFrameReflower // Only if it's the first page, we save the nodes with a fixed position if ($current_page == 0) { - $children = $child->get_children(); - foreach ($children as $onechild) { + foreach ($child->get_children() as $onechild) { if ($onechild->get_style()->position === "fixed") { $fixed_children[] = $onechild->deep_copy(); } @@ -145,13 +146,13 @@ class Page extends AbstractFrameReflower $this->_check_callbacks("begin_page_render", $child); // Render the page - $this->_frame->get_renderer()->render($child); + $frame->get_renderer()->render($child); // Check for end render callback $this->_check_callbacks("end_page_render", $child); if ($next_child) { - $this->_frame->next_page(); + $frame->next_page(); } // Wait to dispose of all frames on the previous page @@ -174,31 +175,24 @@ class Page extends AbstractFrameReflower * Check for callbacks that need to be performed when a given event * gets triggered on a page * - * @param string $event the type of event - * @param Frame $frame the frame that event is triggered on + * @param string $event The type of event + * @param Frame $frame The frame that event is triggered on */ - protected function _check_callbacks($event, $frame) + protected function _check_callbacks(string $event, Frame $frame): void { if (!isset($this->_callbacks)) { - $dompdf = $this->_frame->get_dompdf(); - $this->_callbacks = $dompdf->get_callbacks(); - $this->_canvas = $dompdf->get_canvas(); + $dompdf = $this->get_dompdf(); + $this->_callbacks = $dompdf->getCallbacks(); + $this->_canvas = $dompdf->getCanvas(); } - if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { - $info = array( - 0 => $this->_canvas, "canvas" => $this->_canvas, - 1 => $frame, "frame" => $frame, - ); + if (isset($this->_callbacks[$event])) { $fs = $this->_callbacks[$event]; + $canvas = $this->_canvas; + $fontMetrics = $this->get_dompdf()->getFontMetrics(); + foreach ($fs as $f) { - if (is_callable($f)) { - if (is_array($f)) { - $f[0]->{$f[1]}($info); - } else { - $f($info); - } - } + $f($frame, $canvas, $fontMetrics); } } } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Table.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Table.php new file mode 100644 index 000000000..5173738e9 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Table.php @@ -0,0 +1,523 @@ +_state = null; + parent::__construct($frame); + } + + /** + * State is held here so it needs to be reset along with the decorator + */ + public function reset(): void + { + parent::reset(); + $this->_state = null; + } + + protected function _assign_widths() + { + $style = $this->_frame->get_style(); + + // Find the min/max width of the table and sort the columns into + // absolute/percent/auto arrays + $delta = $this->_state["width_delta"]; + $min_width = $this->_state["min_width"]; + $max_width = $this->_state["max_width"]; + $percent_used = $this->_state["percent_used"]; + $absolute_used = $this->_state["absolute_used"]; + $auto_min = $this->_state["auto_min"]; + + $absolute =& $this->_state["absolute"]; + $percent =& $this->_state["percent"]; + $auto =& $this->_state["auto"]; + + // Determine the actual width of the table (excluding borders and + // padding) + $cb = $this->_frame->get_containing_block(); + $columns =& $this->_frame->get_cellmap()->get_columns(); + + $width = $style->width; + $min_table_width = $this->resolve_min_width($cb["w"]) - $delta; + + if ($width !== "auto") { + $preferred_width = (float) $style->length_in_pt($width, $cb["w"]) - $delta; + + if ($preferred_width < $min_table_width) { + $preferred_width = $min_table_width; + } + + if ($preferred_width > $min_width) { + $width = $preferred_width; + } else { + $width = $min_width; + } + + } else { + if ($max_width + $delta < $cb["w"]) { + $width = $max_width; + } elseif ($cb["w"] - $delta > $min_width) { + $width = $cb["w"] - $delta; + } else { + $width = $min_width; + } + + if ($width < $min_table_width) { + $width = $min_table_width; + } + + } + + // Store our resolved width + $style->set_used("width", $width); + + $cellmap = $this->_frame->get_cellmap(); + + if ($cellmap->is_columns_locked()) { + return; + } + + // If the whole table fits on the page, then assign each column it's max width + if ($width == $max_width) { + foreach ($columns as $i => $col) { + $cellmap->set_column_width($i, $col["max-width"]); + } + + return; + } + + // Determine leftover and assign it evenly to all columns + if ($width > $min_width) { + // We have three cases to deal with: + // + // 1. All columns are auto or absolute width. In this case we + // distribute extra space across all auto columns weighted by the + // difference between their max and min width, or by max width only + // if the width of the table is larger than the max width for all + // columns. + // + // 2. Only absolute widths have been specified, no auto columns. In + // this case we distribute extra space across all columns weighted + // by their absolute width. + // + // 3. Percentage widths have been specified. In this case we normalize + // the percentage values and try to assign widths as fractions of + // the table width. Absolute column widths are fully satisfied and + // any remaining space is evenly distributed among all auto columns. + + // Case 1: + if ($percent_used == 0 && count($auto)) { + foreach ($absolute as $i) { + $w = $columns[$i]["min-width"]; + $cellmap->set_column_width($i, $w); + } + + if ($width < $max_width) { + $increment = $width - $min_width; + $table_delta = $max_width - $min_width; + + foreach ($auto as $i) { + $min = $columns[$i]["min-width"]; + $max = $columns[$i]["max-width"]; + $col_delta = $max - $min; + $w = $min + $increment * ($col_delta / $table_delta); + $cellmap->set_column_width($i, $w); + } + } else { + $increment = $width - $max_width; + $auto_max = $max_width - $absolute_used; + + foreach ($auto as $i) { + $max = $columns[$i]["max-width"]; + $f = $auto_max > 0 ? $max / $auto_max : 1 / count($auto); + $w = $max + $increment * $f; + $cellmap->set_column_width($i, $w); + } + } + return; + } + + // Case 2: + if ($percent_used == 0 && !count($auto)) { + $increment = $width - $absolute_used; + + foreach ($absolute as $i) { + $abs = $columns[$i]["min-width"]; + $f = $absolute_used > 0 ? $abs / $absolute_used : 1 / count($absolute); + $w = $abs + $increment * $f; + $cellmap->set_column_width($i, $w); + } + return; + } + + // Case 3: + if ($percent_used > 0) { + // Scale percent values if the total percentage is > 100 or + // there are no auto values to take up slack + if ($percent_used > 100 || count($auto) == 0) { + $scale = 100 / $percent_used; + } else { + $scale = 1; + } + + // Account for the minimum space used by the unassigned auto + // columns, by the columns with absolute widths, and the + // percentage columns following the current one + $used_width = $auto_min + $absolute_used; + + foreach ($absolute as $i) { + $w = $columns[$i]["min-width"]; + $cellmap->set_column_width($i, $w); + } + + $percent_min = 0; + + foreach ($percent as $i) { + $percent_min += $columns[$i]["min-width"]; + } + + // First-come, first served + foreach ($percent as $i) { + $min = $columns[$i]["min-width"]; + $percent_min -= $min; + $slack = $width - $used_width - $percent_min; + + $columns[$i]["percent"] *= $scale; + $w = min($columns[$i]["percent"] * $width / 100, $slack); + + if ($w < $min) { + $w = $min; + } + + $cellmap->set_column_width($i, $w); + $used_width += $w; + } + + // This works because $used_width includes the min-width of each + // unassigned column + if (count($auto) > 0) { + $increment = ($width - $used_width) / count($auto); + + foreach ($auto as $i) { + $w = $columns[$i]["min-width"] + $increment; + $cellmap->set_column_width($i, $w); + } + } + return; + } + } else { + // We are over-constrained: + // Each column gets its minimum width + foreach ($columns as $i => $col) { + $cellmap->set_column_width($i, $col["min-width"]); + } + } + } + + /** + * Determine the frame's height based on min/max height + * + * @return float + */ + protected function _calculate_height() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + + $height = $style->length_in_pt($style->height, $cb["h"]); + + $cellmap = $frame->get_cellmap(); + $cellmap->assign_frame_heights(); + $rows = $cellmap->get_rows(); + + // Determine our content height + $content_height = 0.0; + foreach ($rows as $r) { + $content_height += $r["height"]; + } + + if ($height === "auto") { + $height = $content_height; + } + + // Handle min/max height + // https://www.w3.org/TR/CSS21/visudet.html#min-max-heights + $min_height = $this->resolve_min_height($cb["h"]); + $max_height = $this->resolve_max_height($cb["h"]); + $height = Helpers::clamp($height, $min_height, $max_height); + + // Use the content height or the height value, whichever is greater + if ($height <= $content_height) { + $height = $content_height; + } else { + // FIXME: Borders and row positions are not properly updated by this + // $cellmap->set_frame_heights($height, $content_height); + } + + return $height; + } + + /** + * @param BlockFrameDecorator|null $block + */ + function reflow(BlockFrameDecorator $block = null) + { + /** @var TableFrameDecorator */ + $frame = $this->_frame; + + // Check if a page break is forced + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + // Bail if the page is full + if ($page->is_full()) { + return; + } + + // Let the page know that we're reflowing a table so that splits + // are suppressed (simply setting page-break-inside: avoid won't + // work because we may have an arbitrary number of block elements + // inside tds.) + $page->table_reflow_start(); + + $this->determine_absolute_containing_block(); + + // Counters and generated content + $this->_set_content(); + + // Collapse vertical margins, if required + $this->_collapse_margins(); + + // Table layout algorithm: + // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout + + if (is_null($this->_state)) { + $this->get_min_max_width(); + } + + $cb = $frame->get_containing_block(); + $style = $frame->get_style(); + + // This is slightly inexact, but should be okay. Add half the + // border-spacing to the table as padding. The other half is added to + // the cells themselves. + if ($style->border_collapse === "separate") { + [$h, $v] = $style->border_spacing; + $v = $v / 2; + $h = $h / 2; + + $style->set_used("padding_left", (float)$style->length_in_pt($style->padding_left, $cb["w"]) + $h); + $style->set_used("padding_right", (float)$style->length_in_pt($style->padding_right, $cb["w"]) + $h); + $style->set_used("padding_top", (float)$style->length_in_pt($style->padding_top, $cb["w"]) + $v); + $style->set_used("padding_bottom", (float)$style->length_in_pt($style->padding_bottom, $cb["w"]) + $v); + } + + $this->_assign_widths(); + + // Adjust left & right margins, if they are auto + $delta = $this->_state["width_delta"]; + $width = $style->width; + $left = $style->length_in_pt($style->margin_left, $cb["w"]); + $right = $style->length_in_pt($style->margin_right, $cb["w"]); + + $diff = (float) $cb["w"] - (float) $width - $delta; + + if ($left === "auto" && $right === "auto") { + if ($diff < 0) { + $left = 0; + $right = $diff; + } else { + $left = $right = $diff / 2; + } + } else { + if ($left === "auto") { + $left = max($diff - $right, 0); + } + if ($right === "auto") { + $right = max($diff - $left, 0); + } + } + + $style->set_used("margin_left", $left); + $style->set_used("margin_right", $right); + + $frame->position(); + [$x, $y] = $frame->get_position(); + + // Determine the content edge + $offset_x = (float)$left + (float)$style->length_in_pt([ + $style->padding_left, + $style->border_left_width + ], $cb["w"]); + $offset_y = (float)$style->length_in_pt([ + $style->margin_top, + $style->border_top_width, + $style->padding_top + ], $cb["w"]); + $content_x = $x + $offset_x; + $content_y = $y + $offset_y; + + if (isset($cb["h"])) { + $h = $cb["h"]; + } else { + $h = null; + } + + $cellmap = $frame->get_cellmap(); + $col =& $cellmap->get_column(0); + $col["x"] = $offset_x; + + $row =& $cellmap->get_row(0); + $row["y"] = $offset_y; + + $cellmap->assign_x_positions(); + + // Set the containing block of each child & reflow + foreach ($frame->get_children() as $child) { + $child->set_containing_block($content_x, $content_y, $width, $h); + $child->reflow(); + + if (!$page->in_nested_table()) { + // Check if a split has occurred + $page->check_page_break($child); + + if ($page->is_full()) { + break; + } + } + } + + // Stop reflow if a page break has occurred before the frame, in which + // case it has been reset, including its position + if ($page->is_full() && $frame->get_position("x") === null) { + $page->table_reflow_end(); + return; + } + + // Assign heights to our cells: + $style->set_used("height", $this->_calculate_height()); + + $page->table_reflow_end(); + + if ($block && $frame->is_in_flow()) { + $block->add_frame_to_line($frame); + + if ($frame->is_block_level()) { + $block->add_line(); + } + } + } + + public function get_min_max_width(): array + { + if (!is_null($this->_min_max_cache)) { + return $this->_min_max_cache; + } + + $style = $this->_frame->get_style(); + $cellmap = $this->_frame->get_cellmap(); + + $this->_frame->normalize(); + + // Add the cells to the cellmap (this will calculate column widths as + // frames are added) + $cellmap->add_frame($this->_frame); + + // Find the min/max width of the table and sort the columns into + // absolute/percent/auto arrays + $this->_state = []; + $this->_state["min_width"] = 0; + $this->_state["max_width"] = 0; + + $this->_state["percent_used"] = 0; + $this->_state["absolute_used"] = 0; + $this->_state["auto_min"] = 0; + + $this->_state["absolute"] = []; + $this->_state["percent"] = []; + $this->_state["auto"] = []; + + $columns =& $cellmap->get_columns(); + foreach ($columns as $i => $col) { + $this->_state["min_width"] += $col["min-width"]; + $this->_state["max_width"] += $col["max-width"]; + + if ($col["absolute"] > 0) { + $this->_state["absolute"][] = $i; + $this->_state["absolute_used"] += $col["min-width"]; + } elseif ($col["percent"] > 0) { + $this->_state["percent"][] = $i; + $this->_state["percent_used"] += $col["percent"]; + } else { + $this->_state["auto"][] = $i; + $this->_state["auto_min"] += $col["min-width"]; + } + } + + // Account for margins, borders, padding, and border spacing + $cb_w = $this->_frame->get_containing_block("w"); + $lm = (float) $style->length_in_pt($style->margin_left, $cb_w); + $rm = (float) $style->length_in_pt($style->margin_right, $cb_w); + + $dims = [ + $style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right + ]; + + if ($style->border_collapse !== "collapse") { + list($dims[]) = $style->border_spacing; + } + + $delta = (float) $style->length_in_pt($dims, $cb_w); + + $this->_state["width_delta"] = $delta; + + $min_width = $this->_state["min_width"] + $delta + $lm + $rm; + $max_width = $this->_state["max_width"] + $delta + $lm + $rm; + + return $this->_min_max_cache = [ + $min_width, + $max_width, + "min" => $min_width, + "max" => $max_width + ]; + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableCell.php similarity index 55% rename from library/vendor/dompdf/src/FrameReflower/TableCell.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableCell.php index 87c5cf818..f5ce35da6 100644 --- a/library/vendor/dompdf/src/FrameReflower/TableCell.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableCell.php @@ -1,14 +1,14 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameReflower; use Dompdf\FrameDecorator\Block as BlockFrameDecorator; use Dompdf\FrameDecorator\Table as TableFrameDecorator; +use Dompdf\Helpers; /** * Reflows table cells @@ -31,6 +31,9 @@ class TableCell extends Block */ function reflow(BlockFrameDecorator $block = null) { + // Counters and generated content + $this->_set_content(); + $style = $this->_frame->get_style(); $table = TableFrameDecorator::find_parent_table($this->_frame); @@ -50,26 +53,27 @@ class TableCell extends Block //FIXME? $h = $this->_frame->get_containing_block("h"); - $left_space = (float)$style->length_in_pt(array($style->margin_left, + $left_space = (float)$style->length_in_pt([$style->margin_left, $style->padding_left, - $style->border_left_width), + $style->border_left_width], $w); - $right_space = (float)$style->length_in_pt(array($style->padding_right, + $right_space = (float)$style->length_in_pt([$style->padding_right, $style->margin_right, - $style->border_right_width), + $style->border_right_width], $w); - $top_space = (float)$style->length_in_pt(array($style->margin_top, + $top_space = (float)$style->length_in_pt([$style->margin_top, $style->padding_top, - $style->border_top_width), + $style->border_top_width], $h); - $bottom_space = (float)$style->length_in_pt(array($style->margin_bottom, + $bottom_space = (float)$style->length_in_pt([$style->margin_bottom, $style->padding_bottom, - $style->border_bottom_width), + $style->border_bottom_width], $h); - $style->width = $cb_w = $w - $left_space - $right_space; + $cb_w = $w - $left_space - $right_space; + $style->set_used("width", $cb_w); $content_x = $x + $left_space; $content_y = $line_y = $y + $top_space; @@ -86,22 +90,25 @@ class TableCell extends Block // Set the containing blocks and reflow each child foreach ($this->_frame->get_children() as $child) { - if ($page->is_full()) { - break; - } - $child->set_containing_block($content_x, $content_y, $cb_w, $h); $this->process_clear($child); $child->reflow($this->_frame); - $this->process_float($child, $x + $left_space, $w - $right_space - $left_space); + $this->process_float($child, $content_x, $cb_w); + + if ($page->is_full()) { + break; + } } // Determine our height $style_height = (float)$style->length_in_pt($style->height, $h); - $this->_frame->set_content_height($this->_calculate_content_height()); + /** @var FrameDecorator\TableCell */ + $frame = $this->_frame; - $height = max($style_height, (float)$this->_frame->get_content_height()); + $frame->set_content_height($this->_calculate_content_height()); + + $height = max($style_height, (float)$frame->get_content_height()); // Let the cellmap know our height $cell_height = $height / count($cells["rows"]); @@ -114,8 +121,41 @@ class TableCell extends Block $cellmap->set_row_height($i, $cell_height); } - $style->height = $height; + $style->set_used("height", $height); + $this->_text_align(); $this->vertical_align(); + + // Handle relative positioning + foreach ($this->_frame->get_children() as $child) { + $this->position_relative($child); + } + } + + public function get_min_max_content_width(): array + { + // Ignore percentage values for a specified width here, as they are + // relative to the table width, which is not determined yet + $style = $this->_frame->get_style(); + $width = $style->width; + $fixed_width = $width !== "auto" && !Helpers::is_percent($width); + + [$min, $max] = $this->get_min_max_child_width(); + + // For table cells: Use specified width if it is greater than the + // minimum defined by the content + if ($fixed_width) { + $width = (float) $style->length_in_pt($width, 0); + $min = max($width, $min); + $max = $min; + } + + // Handle min/max width style properties + $min_width = $this->resolve_min_width(null); + $max_width = $this->resolve_max_width(null); + $min = Helpers::clamp($min, $min_width, $max_width); + $max = Helpers::clamp($max, $min_width, $max_width); + + return [$min, $max]; } } diff --git a/library/vendor/dompdf/src/FrameReflower/TableRow.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRow.php similarity index 72% rename from library/vendor/dompdf/src/FrameReflower/TableRow.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRow.php index 5b94473b9..f84c1258b 100644 --- a/library/vendor/dompdf/src/FrameReflower/TableRow.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRow.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\FrameReflower; @@ -33,23 +32,32 @@ class TableRow extends AbstractFrameReflower */ function reflow(BlockFrameDecorator $block = null) { - $page = $this->_frame->get_root(); + /** @var TableRowFrameDecorator */ + $frame = $this->_frame; + // Check if a page break is forced + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + // Bail if the page is full if ($page->is_full()) { return; } + // Counters and generated content + $this->_set_content(); + $this->_frame->position(); $style = $this->_frame->get_style(); $cb = $this->_frame->get_containing_block(); foreach ($this->_frame->get_children() as $child) { - if ($page->is_full()) { - return; - } - $child->set_containing_block($cb); $child->reflow(); + + if ($page->is_full()) { + break; + } } if ($page->is_full()) { @@ -58,8 +66,8 @@ class TableRow extends AbstractFrameReflower $table = TableFrameDecorator::find_parent_table($this->_frame); $cellmap = $table->get_cellmap(); - $style->width = $cellmap->get_frame_width($this->_frame); - $style->height = $cellmap->get_frame_height($this->_frame); + $style->set_used("width", $cellmap->get_frame_width($this->_frame)); + $style->set_used("height", $cellmap->get_frame_height($this->_frame)); $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); } @@ -67,7 +75,7 @@ class TableRow extends AbstractFrameReflower /** * @throws Exception */ - function get_min_max_width() + public function get_min_max_width(): array { throw new Exception("Min/max width is undefined for table rows"); } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php new file mode 100644 index 000000000..c8b19aa53 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/TableRowGroup.php @@ -0,0 +1,71 @@ +_frame; + $page = $frame->get_root(); + + // Counters and generated content + $this->_set_content(); + + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + + foreach ($frame->get_children() as $child) { + $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]); + $child->reflow(); + + // Check if a split has occurred + $page->check_page_break($child); + + if ($page->is_full()) { + break; + } + } + + $table = TableFrameDecorator::find_parent_table($frame); + $cellmap = $table->get_cellmap(); + + // Stop reflow if a page break has occurred before the frame, in which + // case it is not part of its parent table's cell map yet + if ($page->is_full() && !$cellmap->frame_exists_in_cellmap($frame)) { + return; + } + + $style->set_used("width", $cellmap->get_frame_width($frame)); + $style->set_used("height", $cellmap->get_frame_height($frame)); + + $frame->set_position($cellmap->get_frame_position($frame)); + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Text.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Text.php new file mode 100644 index 000000000..57439ae70 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/FrameReflower/Text.php @@ -0,0 +1,605 @@ + + */ + const SOFT_HYPHEN = "\xC2\xAD"; + + /** + * The regex splits on everything that's a separator (^\S double negative), + * excluding the following non-breaking space characters: + * * nbsp (\xA0) + * * narrow nbsp (\x{202F}) + * * figure space (\x{2007}) + */ + public static $_whitespace_pattern = '/([^\S\xA0\x{202F}\x{2007}]+)/u'; + + /** + * The regex splits on everything that's a separator (^\S double negative) + * plus dashes, excluding the following non-breaking space characters: + * * nbsp (\xA0) + * * narrow nbsp (\x{202F}) + * * figure space (\x{2007}) + */ + public static $_wordbreak_pattern = '/([^\S\xA0\x{202F}\x{2007}\n]+|\R|\-+|\xAD+)/u'; + + /** + * Frame for this reflower + * + * @var TextFrameDecorator + */ + protected $_frame; + + /** + * Saves trailing whitespace trimmed after a line break, so it can be + * restored when needed. + * + * @var string|null + */ + protected $trailingWs = null; + + /** + * @var FontMetrics + */ + private $fontMetrics; + + /** + * @param TextFrameDecorator $frame + * @param FontMetrics $fontMetrics + */ + public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics) + { + parent::__construct($frame); + $this->setFontMetrics($fontMetrics); + } + + /** + * Apply text transform and white-space collapse according to style. + * + * * http://www.w3.org/TR/CSS21/text.html#propdef-text-transform + * * http://www.w3.org/TR/CSS21/text.html#propdef-white-space + * + * @param string $text + * @return string + */ + protected function pre_process_text(string $text): string + { + $style = $this->_frame->get_style(); + + // Handle text transform + switch ($style->text_transform) { + case "capitalize": + $text = Helpers::mb_ucwords($text); + break; + case "uppercase": + $text = mb_convert_case($text, MB_CASE_UPPER); + break; + case "lowercase": + $text = mb_convert_case($text, MB_CASE_LOWER); + break; + default: + break; + } + + // Handle white-space collapse + switch ($style->white_space) { + default: + case "normal": + case "nowrap": + $text = preg_replace(self::$_whitespace_pattern, " ", $text) ?? ""; + break; + + case "pre-line": + // Collapse white space except for line breaks + $text = preg_replace('/([^\S\xA0\x{202F}\x{2007}\n]+)/u', " ", $text) ?? ""; + break; + + case "pre": + case "pre-wrap": + break; + + } + + return $text; + } + + /** + * @param string $text + * @param BlockFrameDecorator $block + * @param bool $nowrap + * + * @return bool|int + */ + protected function line_break(string $text, BlockFrameDecorator $block, bool $nowrap = false) + { + $fontMetrics = $this->getFontMetrics(); + $frame = $this->_frame; + $style = $frame->get_style(); + $font = $style->font_family; + $size = $style->font_size; + $word_spacing = $style->word_spacing; + $letter_spacing = $style->letter_spacing; + + // Determine the available width + $current_line = $block->get_current_line_box(); + $line_width = $frame->get_containing_block("w"); + $current_line_width = $current_line->left + $current_line->w + $current_line->right; + $available_width = $line_width - $current_line_width; + + // Determine the frame width including margin, padding & border + $visible_text = preg_replace('/\xAD/u', "", $text); + $text_width = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing); + $mbp_width = (float) $style->length_in_pt([ + $style->margin_left, + $style->border_left_width, + $style->padding_left, + $style->padding_right, + $style->border_right_width, + $style->margin_right + ], $line_width); + $frame_width = $text_width + $mbp_width; + + if (Helpers::lengthLessOrEqual($frame_width, $available_width)) { + return false; + } + + if ($nowrap) { + return $current_line_width == 0 ? false : 0; + } + + // Split the text into words + $words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE); + $wc = count($words); + + // Determine the split point + $width = 0.0; + $str = ""; + + $space_width = $fontMetrics->getTextWidth(" ", $font, $size, $word_spacing, $letter_spacing); + $shy_width = $fontMetrics->getTextWidth(self::SOFT_HYPHEN, $font, $size); + + // @todo support + for ($i = 0; $i < $wc; $i += 2) { + // Allow trailing white space to overflow. White space is always + // collapsed to the standard space character currently, so only + // handle that for now + $sep = $words[$i + 1] ?? ""; + $word = $sep === " " ? $words[$i] : $words[$i] . $sep; + $word_width = $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing); + $used_width = $width + $word_width + $mbp_width; + + if (Helpers::lengthGreater($used_width, $available_width)) { + // If the previous split happened by soft hyphen, we have to + // append its width again because the last hyphen of a line + // won't be removed + if (isset($words[$i - 1]) && self::SOFT_HYPHEN === $words[$i - 1]) { + $width += $shy_width; + } + break; + } + + // If the word is splitted by soft hyphen, but no line break is needed + // we have to reduce the width. But the str is not modified, otherwise + // the wrong offset is calculated at the end of this method. + if ($sep === self::SOFT_HYPHEN) { + $width += $word_width - $shy_width; + $str .= $word; + } elseif ($sep === " ") { + $width += $word_width + $space_width; + $str .= $word . $sep; + } else { + $width += $word_width; + $str .= $word; + } + } + + // The first word has overflowed. Force it onto the line, or as many + // characters as fit if breaking words is allowed + if ($current_line_width == 0 && $width === 0.0) { + if ($sep === " ") { + $word .= $sep; + } + + // https://www.w3.org/TR/css-text-3/#overflow-wrap-property + $wrap = $style->overflow_wrap; + $break_word = $wrap === "anywhere" || $wrap === "break-word"; + + if ($break_word) { + $s = ""; + + for ($j = 0; $j < mb_strlen($word); $j++) { + $c = mb_substr($word, $j, 1); + $w = $fontMetrics->getTextWidth($s . $c, $font, $size, $word_spacing, $letter_spacing); + + if (Helpers::lengthGreater($w, $available_width)) { + break; + } + + $s .= $c; + } + + // Always force the first character onto the line + $str = $j === 0 ? $s . $c : $s; + } else { + $str = $word; + } + } + + $offset = mb_strlen($str); + return $offset; + } + + /** + * @param string $text + * @return bool|int + */ + protected function newline_break(string $text) + { + if (($i = mb_strpos($text, "\n")) === false) { + return false; + } + + return $i + 1; + } + + /** + * @param BlockFrameDecorator $block + * @return bool|null Whether to add a new line at the end. `null` if reflow + * should be stopped. + */ + protected function layout_line(BlockFrameDecorator $block): ?bool + { + $frame = $this->_frame; + $style = $frame->get_style(); + $current_line = $block->get_current_line_box(); + $text = $frame->get_text(); + + // Trim leading white space if this is the first text on the line + if ($current_line->w === 0.0 && !$frame->is_pre()) { + $text = ltrim($text, " "); + } + + // Exclude wrapped white space. This handles white space between block + // elements in case white space is collapsed + if ($text === "") { + $frame->set_text(""); + $style->set_used("width", 0.0); + return null; + } + + // Determine the next line break + // http://www.w3.org/TR/CSS21/text.html#propdef-white-space + $white_space = $style->white_space; + $nowrap = $white_space === "nowrap" || $white_space === "pre"; + + switch ($white_space) { + default: + case "normal": + case "nowrap": + $split = $this->line_break($text, $block, $nowrap); + $add_line = false; + break; + + case "pre": + case "pre-line": + case "pre-wrap": + $hard_split = $this->newline_break($text); + $first_line = $hard_split !== false + ? mb_substr($text, 0, $hard_split) + : $text; + $soft_split = $this->line_break($first_line, $block, $nowrap); + + $split = $soft_split !== false ? $soft_split : $hard_split; + $add_line = $hard_split !== false; + break; + } + + if ($split === 0) { + // Make sure to move text when floating frames leave no space to + // place anything onto the line + // TODO: Would probably be better to move just below the current + // floating frame instead of trying to place text in line-height + // increments + if ($current_line->h === 0.0) { + // Line height might be 0 + $h = max($frame->get_margin_height(), 1.0); + $block->maximize_line_height($h, $frame); + } + + // Break line and repeat layout + $block->add_line(); + + // Find the appropriate inline ancestor to split + $child = $frame; + $p = $child->get_parent(); + while ($p instanceof InlineFrameDecorator && !$child->get_prev_sibling()) { + $child = $p; + $p = $p->get_parent(); + } + + if ($p instanceof InlineFrameDecorator) { + // Split parent and stop current reflow. Reflow continues + // via child-reflow loop of split parent + $p->split($child); + return null; + } + + return $this->layout_line($block); + } + + // Final split point is determined + if ($split !== false && $split < mb_strlen($text)) { + // Split the line + $frame->set_text($text); + $frame->split_text($split); + $add_line = true; + + // Remove inner soft hyphens + $t = $frame->get_text(); + $shyPosition = mb_strpos($t, self::SOFT_HYPHEN); + if (false !== $shyPosition && $shyPosition < mb_strlen($t) - 1) { + $t = str_replace(self::SOFT_HYPHEN, "", mb_substr($t, 0, -1)) . mb_substr($t, -1); + $frame->set_text($t); + } + } else { + // No split required + // Remove soft hyphens + $text = str_replace(self::SOFT_HYPHEN, "", $text); + $frame->set_text($text); + } + + // Set our new width + $frame->recalculate_width(); + + return $add_line; + } + + /** + * @param BlockFrameDecorator|null $block + */ + function reflow(BlockFrameDecorator $block = null) + { + $frame = $this->_frame; + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + if ($page->is_full()) { + return; + } + + // Determine the text height + $style = $frame->get_style(); + $size = $style->font_size; + $font = $style->font_family; + $font_height = $this->getFontMetrics()->getFontHeight($font, $size); + $style->set_used("height", $font_height); + + // Handle text transform and white space + $text = $this->pre_process_text($frame->get_text()); + $frame->set_text($text); + + $add_line = $this->layout_line($block); + + if ($add_line === null) { + return; + } + + $frame->position(); + + if ($block) { + $line = $block->add_frame_to_line($frame); + $trimmed = trim($frame->get_text()); + + // Split the text into words (used to determine spacing between + // words on justified lines) + if ($trimmed !== "") { + $words = preg_split(self::$_whitespace_pattern, $trimmed); + $line->wc += count($words); + } + + if ($add_line) { + $block->add_line(); + } + } + } + + /** + * Trim trailing white space from the frame text. + */ + public function trim_trailing_ws(): void + { + $frame = $this->_frame; + $text = $frame->get_text(); + $trailing = mb_substr($text, -1); + + // White space is always collapsed to the standard space character + // currently, so only handle that for now + if ($trailing === " ") { + $this->trailingWs = $trailing; + $frame->set_text(mb_substr($text, 0, -1)); + $frame->recalculate_width(); + } + } + + public function reset(): void + { + parent::reset(); + + // Restore trimmed trailing white space, as the frame will go through + // another reflow and line breaks might be different after a split + if ($this->trailingWs !== null) { + $text = $this->_frame->get_text(); + $this->_frame->set_text($text . $this->trailingWs); + $this->trailingWs = null; + } + } + + //........................................................................ + + public function get_min_max_width(): array + { + $fontMetrics = $this->getFontMetrics(); + $frame = $this->_frame; + $style = $frame->get_style(); + $text = $frame->get_text(); + $font = $style->font_family; + $size = $style->font_size; + $word_spacing = $style->word_spacing; + $letter_spacing = $style->letter_spacing; + + // Handle text transform and white space + $text = $this->pre_process_text($frame->get_text()); + + if (!$frame->is_pre()) { + // Determine whether the frame is at the start of its parent block. + // Trim leading white space in that case + $child = $frame; + $p = $frame->get_parent(); + while (!$p->is_block() && !$child->get_prev_sibling()) { + $child = $p; + $p = $p->get_parent(); + } + + if (!$child->get_prev_sibling()) { + $text = ltrim($text, " "); + } + + // Determine whether the frame is at the end of its parent block. + // Trim trailing white space in that case + $child = $frame; + $p = $frame->get_parent(); + while (!$p->is_block() && !$child->get_next_sibling()) { + $child = $p; + $p = $p->get_parent(); + } + + if (!$child->get_next_sibling()) { + $text = rtrim($text, " "); + } + } + + // Strip soft hyphens for max-line-width calculations + $visible_text = preg_replace('/\xAD/u', "", $text); + + // Determine minimum text width + switch ($style->white_space) { + default: + case "normal": + case "pre-line": + case "pre-wrap": + // The min width is the longest word or, if breaking words is + // allowed with the `anywhere` keyword, the widest character. + // For performance reasons, we only check the first character in + // the latter case. + // https://www.w3.org/TR/css-text-3/#overflow-wrap-property + if ($style->overflow_wrap === "anywhere") { + $char = mb_substr($visible_text, 0, 1); + $min = $fontMetrics->getTextWidth($char, $font, $size, $word_spacing, $letter_spacing); + } else { + // Find the longest word + $words = preg_split(self::$_wordbreak_pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE); + $lengths = array_map(function ($chunk) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) { + // Allow trailing white space to overflow. As in actual + // layout above, only handle a single space for now + $sep = $chunk[1] ?? ""; + $word = $sep === " " ? $chunk[0] : $chunk[0] . $sep; + return $fontMetrics->getTextWidth($word, $font, $size, $word_spacing, $letter_spacing); + }, array_chunk($words, 2)); + $min = max($lengths); + } + break; + + case "pre": + // Find the longest line + $lines = array_flip(preg_split("/\R/u", $visible_text)); + array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) { + $chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing); + }); + arsort($lines); + $min = reset($lines); + break; + + case "nowrap": + $min = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing); + break; + } + + // Determine maximum text width + switch ($style->white_space) { + default: + case "normal": + $max = $fontMetrics->getTextWidth($visible_text, $font, $size, $word_spacing, $letter_spacing); + break; + + case "pre-line": + case "pre-wrap": + // Find the longest line + $lines = array_flip(preg_split("/\R/u", $visible_text)); + array_walk($lines, function (&$chunked_text_width, $chunked_text) use ($fontMetrics, $font, $size, $word_spacing, $letter_spacing) { + $chunked_text_width = $fontMetrics->getTextWidth($chunked_text, $font, $size, $word_spacing, $letter_spacing); + }); + arsort($lines); + $max = reset($lines); + break; + + case "pre": + case "nowrap": + $max = $min; + break; + } + + // Account for margins, borders, and padding + $dims = [ + $style->padding_left, + $style->padding_right, + $style->border_left_width, + $style->border_right_width, + $style->margin_left, + $style->margin_right + ]; + + // The containing block is not defined yet, treat percentages as 0 + $delta = (float) $style->length_in_pt($dims, 0); + $min += $delta; + $max += $delta; + + return [$min, $max, "min" => $min, "max" => $max]; + } + + /** + * @param FontMetrics $fontMetrics + * @return $this + */ + public function setFontMetrics(FontMetrics $fontMetrics) + { + $this->fontMetrics = $fontMetrics; + return $this; + } + + /** + * @return FontMetrics + */ + public function getFontMetrics() + { + return $this->fontMetrics; + } +} diff --git a/library/vendor/dompdf/src/Helpers.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Helpers.php similarity index 66% rename from library/vendor/dompdf/src/Helpers.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Helpers.php index ab05b6a74..ba78515a4 100644 --- a/library/vendor/dompdf/src/Helpers.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Helpers.php @@ -1,4 +1,9 @@ 1 && $url[0] !== '\\' && $url[1] !== ':'))) { - // For rel path and local acess we ignore the host, and run the path through realpath() + // For rel path and local access we ignore the host, and run the path through realpath() $ret .= realpath($base_path) . '/'; } $ret .= $url; $ret = preg_replace('/\?(.*)$/', "", $ret); + + $filepath = realpath($ret); + if ($filepath === false) { + return null; + } + + $ret = "$protocol$filepath$res"; + return $ret; } + $ret = $protocol; // Protocol relative urls (e.g. "//example.org/style.css") if (strpos($url, '//') === 0) { $ret .= substr($url, 2); @@ -96,6 +136,27 @@ class Helpers $ret .= $host . $base_path . $url; } + // URL should now be complete, final cleanup + $parsed_url = parse_url($ret); + + // reproduced from https://www.php.net/manual/en/function.parse-url.php#106731 + $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; + $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; + $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; + $user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; + $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; + $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; + $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; + + // partially reproduced from https://stackoverflow.com/a/1243431/264628 + /* replace '//' or '/./' or '/foo/../' with '/' */ + $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'); + for ($n=1; $n>0; $path=preg_replace($re, '/', $path, -1, $n)) {} + + $ret = "$scheme$user$pass$host$port$path$query$fragment"; + return $ret; } @@ -127,27 +188,32 @@ class Helpers } /** - * Converts decimal numbers to roman numerals + * Converts decimal numbers to roman numerals. * - * @param int $num + * As numbers larger than 3999 (and smaller than 1) cannot be represented in + * the standard form of roman numerals, those are left in decimal form. + * + * See https://en.wikipedia.org/wiki/Roman_numerals#Standard_form + * + * @param int|string $num * * @throws Exception * @return string */ - public static function dec2roman($num) + public static function dec2roman($num): string { - static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"); - static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"); - static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"); - static $thou = array("", "m", "mm", "mmm"); + static $ones = ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"]; + static $tens = ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"]; + static $hund = ["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"]; + static $thou = ["", "m", "mm", "mmm"]; if (!is_numeric($num)) { throw new Exception("dec2roman() requires a numeric argument."); } - if ($num > 4000 || $num < 0) { - return "(out of range)"; + if ($num >= 4000 || $num <= 0) { + return (string) $num; } $num = strrev((string)$num); @@ -173,16 +239,32 @@ class Helpers return $ret; } + /** + * Restrict a length to the given range. + * + * If min > max, the result is min. + * + * @param float $length + * @param float $min + * @param float $max + * + * @return float + */ + public static function clamp(float $length, float $min, float $max): float + { + return max($min, min($length, $max)); + } + /** * Determines whether $value is a percentage or not * - * @param float $value + * @param string|float|int $value * * @return bool */ - public static function is_percent($value) + public static function is_percent($value): bool { - return false !== mb_strpos($value, "%"); + return is_string($value) && false !== mb_strpos($value, "%"); } /** @@ -200,11 +282,11 @@ class Helpers } $match['data'] = rawurldecode($match['data']); - $result = array( + $result = [ 'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII', 'mime' => $match['mime'] ? $match['mime'] : 'text/plain', 'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'], - ); + ]; return $result; } @@ -226,18 +308,18 @@ class Helpers * @return string The original URL with special characters encoded */ public static function encodeURI($uri) { - $unescaped = array( + $unescaped = [ '%2D'=>'-','%5F'=>'_','%2E'=>'.','%21'=>'!', '%7E'=>'~', '%2A'=>'*', '%27'=>"'", '%28'=>'(', '%29'=>')' - ); - $reserved = array( + ]; + $reserved = [ '%3B'=>';','%2C'=>',','%2F'=>'/','%3F'=>'?','%3A'=>':', '%40'=>'@','%26'=>'&','%3D'=>'=','%2B'=>'+','%24'=>'$' - ); - $score = array( + ]; + $score = [ '%23'=>'#' - ); - return strtr(rawurlencode(rawurldecode($uri)), array_merge($reserved,$unescaped,$score)); + ]; + return strtr(rawurlencode(rawurldecode($uri)), array_merge($reserved, $unescaped, $score)); } /** @@ -245,7 +327,7 @@ class Helpers * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp * * @param string $str Data to decode - * @param integer $width Image width + * @param int $width Image width * * @return string */ @@ -298,7 +380,7 @@ class Helpers * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp * * @param string $str Data to decode - * @param integer $width Image width + * @param int $width Image width * * @return string */ @@ -306,7 +388,7 @@ class Helpers { $w = floor($width / 2) + ($width % 2); $lineWidth = $w + (3 - (($width - 1) / 2) % 4); - $pixels = array(); + $pixels = []; $cnt = strlen($str); $c = 0; @@ -380,14 +462,14 @@ class Helpers $host = ""; $path = ""; $file = ""; + $res = ""; $arr = parse_url($url); if ( isset($arr["scheme"]) ) { $arr["scheme"] = mb_strtolower($arr["scheme"]); } - // Exclude windows drive letters... - if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1) { + if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && $arr["scheme"] !== "phar" && strlen($arr["scheme"]) > 1) { $protocol = $arr["scheme"] . "://"; if (isset($arr["user"])) { @@ -429,42 +511,32 @@ class Helpers } else { - $i = mb_stripos($url, "file://"); - if ($i !== false) { - $url = mb_substr($url, $i + 7); - } - - $protocol = ""; // "file://"; ? why doesn't this work... It's because of - // network filenames like //COMPU/SHARENAME - + $protocol = ""; $host = ""; // localhost, really - $file = basename($url); - - $path = dirname($url); - - // Check that the path exists - if ($path !== false) { - $path .= '/'; + $i = mb_stripos($url, "://"); + if ($i !== false) { + $protocol = mb_strtolower(mb_substr($url, 0, $i + 3)); + $url = mb_substr($url, $i + 3); } else { - // generate a url to access the file if no real path found. - $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://'; - - $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n"); - - if (substr($arr["path"], 0, 1) === '/') { - $path = dirname($arr["path"]); - } else { - $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"]; - } + $protocol = "file://"; } + + if ($protocol === "phar://") { + $res = substr($url, stripos($url, ".phar")+5); + $url = substr($url, 7, stripos($url, ".phar")-2); + } + + $file = basename($url); + $path = dirname($url) . "/"; } - $ret = array($protocol, $host, $path, $file, + $ret = [$protocol, $host, $path, $file, "protocol" => $protocol, "host" => $host, "path" => $path, - "file" => $file); + "file" => $file, + "resource" => $res]; return $ret; } @@ -503,7 +575,7 @@ class Helpers public static function record_warnings($errno, $errstr, $errfile, $errline) { // Not a warning or notice - if (!($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING))) { + if (!($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING | E_STRICT | E_DEPRECATED | E_USER_DEPRECATED))) { throw new Exception($errstr . " $errno"); } @@ -525,12 +597,12 @@ class Helpers { if ($c <= 0x7F) { return chr($c); - } else if ($c <= 0x7FF) { + } elseif ($c <= 0x7FF) { return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F); - } else if ($c <= 0xFFFF) { + } elseif ($c <= 0xFFFF) { return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F) . chr(0x80 | $c & 0x3F); - } else if ($c <= 0x10FFFF) { + } elseif ($c <= 0x10FFFF) { return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F) . chr(0x80 | $c >> 6 & 0x3F) . chr(0x80 | $c & 0x3F); @@ -551,7 +623,7 @@ class Helpers public static function cmyk_to_rgb($c, $m = null, $y = null, $k = null) { if (is_array($c)) { - list($c, $m, $y, $k) = $c; + [$c, $m, $y, $k] = $c; } $c *= 255; @@ -573,10 +645,10 @@ class Helpers $b = 0; } - return array( + return [ $r, $g, $b, "r" => $r, "g" => $g, "b" => $b - ); + ]; } /** @@ -584,50 +656,52 @@ class Helpers * * @param string $filename * @param resource $context - * @return array The same format as getimagesize($filename) + * @return array An array of three elements: width and height as + * `float|int`, and image type as `string|null`. */ public static function dompdf_getimagesize($filename, $context = null) { - static $cache = array(); + static $cache = []; if (isset($cache[$filename])) { return $cache[$filename]; } - list($width, $height, $type) = getimagesize($filename); + [$width, $height, $type] = getimagesize($filename); // Custom types - $types = array( + $types = [ IMAGETYPE_JPEG => "jpeg", IMAGETYPE_GIF => "gif", IMAGETYPE_BMP => "bmp", IMAGETYPE_PNG => "png", - ); + IMAGETYPE_WEBP => "webp", + ]; - $type = isset($types[$type]) ? $types[$type] : null; + $type = $types[$type] ?? null; if ($width == null || $height == null) { - list($data, $headers) = Helpers::getFileContent($filename, $context); + [$data] = Helpers::getFileContent($filename, $context); - if (substr($data, 0, 2) === "BM") { - $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data); - $width = (int)$meta['width']; - $height = (int)$meta['height']; - $type = "bmp"; - } - else { - if (strpos($data, "loadFile($filename); - list($width, $height) = $doc->getDimensions(); + [$width, $height] = $doc->getDimensions(); + $width = (float) $width; + $height = (float) $height; $type = "svg"; } } - } - return $cache[$filename] = array($width, $height, $type); + return $cache[$filename] = [$width ?? 0, $height ?? 0, $type]; } /** @@ -693,7 +767,7 @@ class Helpers $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; // read color palette - $palette = array(); + $palette = []; if ($meta['bits'] < 16) { $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); // in rare cases the color value is signed @@ -815,58 +889,119 @@ class Helpers * - curl: if allow_url_fopen is false and curl is available * * @param string $uri - * @param resource $context (ignored if curl is used) + * @param resource $context * @param int $offset - * @param int $maxlen (ignored if curl is used) - * @return bool|array + * @param int $maxlen + * @return string[] */ public static function getFileContent($uri, $context = null, $offset = 0, $maxlen = null) { - $result = false; + $content = null; $headers = null; - list($proto, $host, $path, $file) = Helpers::explode_url($uri); - $is_local_path = ($proto == "" || $proto === "file://"); + [$protocol] = Helpers::explode_url($uri); + $is_local_path = in_array(strtolower($protocol), ["", "file://", "phar://"], true); + $can_use_curl = in_array(strtolower($protocol), ["http://", "https://"], true); - set_error_handler(array("\\Dompdf\\Helpers", "record_warnings")); + set_error_handler([self::class, 'record_warnings']); - if ($is_local_path || ini_get("allow_url_fopen")) { - if ($is_local_path === false) { - $uri = Helpers::encodeURI($uri); + try { + if ($is_local_path || ini_get('allow_url_fopen') || !$can_use_curl) { + if ($is_local_path === false) { + $uri = Helpers::encodeURI($uri); + } + if (isset($maxlen)) { + $result = file_get_contents($uri, false, $context, $offset, $maxlen); + } else { + $result = file_get_contents($uri, false, $context, $offset); + } + if ($result !== false) { + $content = $result; + } + if (isset($http_response_header)) { + $headers = $http_response_header; + } + + } elseif ($can_use_curl && function_exists('curl_exec')) { + $curl = curl_init($uri); + + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HEADER, true); + if ($offset > 0) { + curl_setopt($curl, CURLOPT_RESUME_FROM, $offset); + } + + if ($maxlen > 0) { + curl_setopt($curl, CURLOPT_BUFFERSIZE, 128); + curl_setopt($curl, CURLOPT_NOPROGRESS, false); + curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, function ($res, $download_size_total, $download_size, $upload_size_total, $upload_size) use ($maxlen) { + return ($download_size > $maxlen) ? 1 : 0; + }); + } + + $context_options = []; + if (!is_null($context)) { + $context_options = stream_context_get_options($context); + } + foreach ($context_options as $stream => $options) { + foreach ($options as $option => $value) { + $key = strtolower($stream) . ":" . strtolower($option); + switch ($key) { + case "curl:curl_verify_ssl_host": + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, !$value ? 0 : 2); + break; + case "curl:max_redirects": + curl_setopt($curl, CURLOPT_MAXREDIRS, $value); + break; + case "http:follow_location": + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $value); + break; + case "http:header": + if (is_string($value)) { + curl_setopt($curl, CURLOPT_HTTPHEADER, [$value]); + } else { + curl_setopt($curl, CURLOPT_HTTPHEADER, $value); + } + break; + case "http:timeout": + curl_setopt($curl, CURLOPT_TIMEOUT, $value); + break; + case "http:user_agent": + curl_setopt($curl, CURLOPT_USERAGENT, $value); + break; + case "curl:curl_verify_ssl_peer": + case "ssl:verify_peer": + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $value); + break; + } + } + } + + $data = curl_exec($curl); + + if ($data !== false && !curl_errno($curl)) { + switch ($http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE)) { + case 200: + $raw_headers = substr($data, 0, curl_getinfo($curl, CURLINFO_HEADER_SIZE)); + $headers = preg_split("/[\n\r]+/", trim($raw_headers)); + $content = substr($data, curl_getinfo($curl, CURLINFO_HEADER_SIZE)); + break; + } + } + curl_close($curl); } - if (isset($maxlen)) { - $result = file_get_contents($uri, null, $context, $offset, $maxlen); - } else { - $result = file_get_contents($uri, null, $context, $offset); - } - if (isset($http_response_header)) { - $headers = $http_response_header; - } - - } elseif (function_exists("curl_exec")) { - $curl = curl_init($uri); - - //TODO: use $context to define additional curl options - curl_setopt($curl, CURLOPT_TIMEOUT, 10); - curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HEADER, true); - if ($offset > 0) { - curl_setopt($curl, CURLOPT_RESUME_FROM, $offset); - } - - $data = curl_exec($curl); - $raw_headers = substr($data, 0, curl_getinfo($curl, CURLINFO_HEADER_SIZE)); - $headers = preg_split("/[\n\r]+/", trim($raw_headers)); - $result = substr($data, curl_getinfo($curl, CURLINFO_HEADER_SIZE)); - curl_close($curl); + } finally { + restore_error_handler(); } - restore_error_handler(); - - return array($result, $headers); + return [$content, $headers]; } - public static function mb_ucwords($str) { + /** + * @param string $str + * @return string + */ + public static function mb_ucwords(string $str): string + { $max_len = mb_strlen($str); if ($max_len === 1) { return mb_strtoupper($str); @@ -874,7 +1009,7 @@ class Helpers $str = mb_strtoupper(mb_substr($str, 0, 1)) . mb_substr($str, 1); - foreach (array(' ', '.', ',', '!', '?', '-', '+') as $s) { + foreach ([' ', '.', ',', '!', '?', '-', '+'] as $s) { $pos = 0; while (($pos = mb_strpos($str, $s, $pos)) !== false) { $pos++; @@ -892,4 +1027,67 @@ class Helpers return $str; } + + /** + * Check whether two lengths should be considered equal, accounting for + * inaccuracies in float computation. + * + * The implementation relies on the fact that we are neither dealing with + * very large, nor with very small numbers in layout. Adapted from + * https://floating-point-gui.de/errors/comparison/. + * + * @param float $a + * @param float $b + * + * @return bool + */ + public static function lengthEqual(float $a, float $b): bool + { + // The epsilon results in a precision of at least: + // * 7 decimal digits at around 1 + // * 4 decimal digits at around 1000 (around the size of common paper formats) + // * 2 decimal digits at around 100,000 (100,000pt ~ 35.28m) + static $epsilon = 1e-8; + static $almostZero = 1e-12; + + $diff = abs($a - $b); + + if ($a === $b || $diff < $almostZero) { + return true; + } + + return $diff < $epsilon * max(abs($a), abs($b)); + } + + /** + * Check `$a < $b`, accounting for inaccuracies in float computation. + */ + public static function lengthLess(float $a, float $b): bool + { + return $a < $b && !self::lengthEqual($a, $b); + } + + /** + * Check `$a <= $b`, accounting for inaccuracies in float computation. + */ + public static function lengthLessOrEqual(float $a, float $b): bool + { + return $a <= $b || self::lengthEqual($a, $b); + } + + /** + * Check `$a > $b`, accounting for inaccuracies in float computation. + */ + public static function lengthGreater(float $a, float $b): bool + { + return $a > $b && !self::lengthEqual($a, $b); + } + + /** + * Check `$a >= $b`, accounting for inaccuracies in float computation. + */ + public static function lengthGreaterOrEqual(float $a, float $b): bool + { + return $a >= $b || self::lengthEqual($a, $b); + } } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Image/Cache.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Image/Cache.php new file mode 100644 index 000000000..f337bbb47 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Image/Cache.php @@ -0,0 +1,254 @@ +getAllowedProtocols(); + if (!array_key_exists($protocol, $allowed_protocols)) { + throw new ImageException("Permission denied on $url. The communication protocol is not supported.", E_WARNING); + } + foreach ($allowed_protocols[$protocol]["rules"] as $rule) { + [$result, $message] = $rule($full_url); + if (!$result) { + throw new ImageException("Error loading $url: $message", E_WARNING); + } + } + } + + if ($protocol === "file://") { + $resolved_url = $full_url; + } elseif (isset(self::$_cache[$full_url])) { + $resolved_url = self::$_cache[$full_url]; + } else { + $tmp_dir = $options->getTempDir(); + if (($resolved_url = @tempnam($tmp_dir, "ca_dompdf_img_")) === false) { + throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING); + } + $tempfile = $resolved_url; + + $image = null; + if ($is_data_uri) { + if (($parsed_data_uri = Helpers::parse_data_uri($url)) !== false) { + $image = $parsed_data_uri["data"]; + } + } else { + list($image, $http_response_header) = Helpers::getFileContent($full_url, $options->getHttpContext()); + } + + // Image not found or invalid + if ($image === null) { + $msg = ($is_data_uri ? "Data-URI could not be parsed" : "Image not found"); + throw new ImageException($msg, E_WARNING); + } + + if (@file_put_contents($resolved_url, $image) === false) { + throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING); + } + + self::$_cache[$full_url] = $resolved_url; + } + + // Check if the local file is readable + if (!is_readable($resolved_url) || !filesize($resolved_url)) { + throw new ImageException("Image not readable or empty", E_WARNING); + } + + list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext()); + + if (($width && $height && in_array($type, ["gif", "png", "jpeg", "bmp", "svg","webp"], true)) === false) { + throw new ImageException("Image type unknown", E_WARNING); + } + + if ($type === "svg") { + $parser = xml_parser_create("utf-8"); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); + xml_set_element_handler( + $parser, + function ($parser, $name, $attributes) use ($options, $parsed_url, $full_url) { + if ($name === "image") { + $attributes = array_change_key_case($attributes, CASE_LOWER); + $url = $attributes["xlink:href"] ?? $attributes["href"]; + if (!empty($url)) { + $inner_full_url = Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url); + if ($inner_full_url === $full_url) { + throw new ImageException("SVG self-reference is not allowed", E_WARNING); + } + [$resolved_url, $type, $message] = self::resolve_url($url, $parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options); + if (!empty($message)) { + throw new ImageException("This SVG document references a restricted resource. $message", E_WARNING); + } + } + } + }, + false + ); + + if (($fp = fopen($resolved_url, "r")) !== false) { + while ($line = fread($fp, 8192)) { + xml_parse($parser, $line, false); + } + fclose($fp); + } + xml_parser_free($parser); + } + } catch (ImageException $e) { + if ($tempfile) { + unlink($tempfile); + } + $resolved_url = self::$broken_image; + list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext()); + $message = self::$error_message; + Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine()); + self::$_cache[$full_url] = $resolved_url; + } + + return [$resolved_url, $type, $message]; + } + + /** + * Register a temp file for the given original image file. + * + * @param string $filePath The path of the original image. + * @param string $tempPath The path of the temp file to register. + * @param string $key An optional key to register the temp file at. + */ + static function addTempImage(string $filePath, string $tempPath, string $key = "default"): void + { + if (!isset(self::$tempImages[$filePath])) { + self::$tempImages[$filePath] = []; + } + + self::$tempImages[$filePath][$key] = $tempPath; + } + + /** + * Get the path of a temp file registered for the given original image file. + * + * @param string $filePath The path of the original image. + * @param string $key The key the temp file is registered at. + */ + static function getTempImage(string $filePath, string $key = "default"): ?string + { + return self::$tempImages[$filePath][$key] ?? null; + } + + /** + * Unlink all cached images (i.e. temporary images either downloaded + * or converted) except for the bundled "broken image" + */ + static function clear(bool $debugPng = false) + { + foreach (self::$_cache as $file) { + if ($file === self::$broken_image) { + continue; + } + if ($debugPng) { + print "[clear unlink $file]"; + } + if (file_exists($file)) { + unlink($file); + } + } + + foreach (self::$tempImages as $versions) { + foreach ($versions as $file) { + if ($file === self::$broken_image) { + continue; + } + if ($debugPng) { + print "[unlink temp image $file]"; + } + if (file_exists($file)) { + unlink($file); + } + } + } + + self::$_cache = []; + self::$tempImages = []; + } + + static function detect_type($file, $context = null) + { + list(, , $type) = Helpers::dompdf_getimagesize($file, $context); + + return $type; + } + + static function is_broken($url) + { + return $url === self::$broken_image; + } +} + +if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.svg"))) { + Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.svg"); +} diff --git a/library/vendor/dompdf/src/JavascriptEmbedder.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/JavascriptEmbedder.php similarity index 90% rename from library/vendor/dompdf/src/JavascriptEmbedder.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/JavascriptEmbedder.php index 7a8fce53b..f4b9bc25a 100644 --- a/library/vendor/dompdf/src/JavascriptEmbedder.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/JavascriptEmbedder.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf; diff --git a/library/vendor/dompdf/src/LineBox.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/LineBox.php similarity index 64% rename from library/vendor/dompdf/src/LineBox.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/LineBox.php index 1634fb17d..11b83c15c 100644 --- a/library/vendor/dompdf/src/LineBox.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/LineBox.php @@ -1,14 +1,17 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf; +use Dompdf\FrameDecorator\AbstractFrameDecorator; use Dompdf\FrameDecorator\Block; +use Dompdf\FrameDecorator\ListBullet; use Dompdf\FrameDecorator\Page; +use Dompdf\FrameReflower\Text as TextFrameReflower; +use Dompdf\Positioner\Inline as InlinePositioner; /** * The line box class @@ -27,12 +30,17 @@ class LineBox protected $_block_frame; /** - * @var Frame[] + * @var AbstractFrameDecorator[] */ - protected $_frames = array(); + protected $_frames = []; /** - * @var integer + * @var ListBullet[] + */ + protected $list_markers = []; + + /** + * @var int */ public $wc = 0; @@ -62,20 +70,27 @@ class LineBox public $right = 0.0; /** - * @var Frame + * @var AbstractFrameDecorator */ public $tallest_frame = null; /** * @var bool[] */ - public $floating_blocks = array(); + public $floating_blocks = []; /** * @var bool */ public $br = false; + /** + * Whether the line box contains any inline-positioned frames. + * + * @var bool + */ + public $inline = false; + /** * Class constructor * @@ -85,7 +100,7 @@ class LineBox public function __construct(Block $frame, $y = 0) { $this->_block_frame = $frame; - $this->_frames = array(); + $this->_frames = []; $this->y = $y; $this->get_float_offsets(); @@ -124,7 +139,7 @@ class LineBox $parent = $p; - $childs = array(); + $childs = []; foreach ($floating_frames as $_floating) { $p = $_floating->get_parent(); @@ -139,9 +154,6 @@ class LineBox return $childs; } - /** - * - */ public function get_float_offsets() { static $anti_infinite_loop = 10000; // FIXME smelly hack @@ -243,7 +255,7 @@ class LineBox } /** - * @return Frame[] + * @return AbstractFrameDecorator[] */ function &get_frames() { @@ -251,11 +263,112 @@ class LineBox } /** - * @param Frame $frame + * @param AbstractFrameDecorator $frame */ - public function add_frame(Frame $frame) + public function add_frame(Frame $frame): void { $this->_frames[] = $frame; + + if ($frame->get_positioner() instanceof InlinePositioner) { + $this->inline = true; + } + } + + /** + * Remove the frame at the given index and all following frames from the + * line. + * + * @param int $index + */ + public function remove_frames(int $index): void + { + $lastIndex = count($this->_frames) - 1; + + if ($index < 0 || $index > $lastIndex) { + return; + } + + for ($i = $lastIndex; $i >= $index; $i--) { + $f = $this->_frames[$i]; + unset($this->_frames[$i]); + $this->w -= $f->get_margin_width(); + } + + // Reset array indices + $this->_frames = array_values($this->_frames); + + // Recalculate the height of the line + $h = 0.0; + $this->inline = false; + + foreach ($this->_frames as $f) { + $h = max($h, $f->get_margin_height()); + + if ($f->get_positioner() instanceof InlinePositioner) { + $this->inline = true; + } + } + + $this->h = $h; + } + + /** + * Get the `outside` positioned list markers to be vertically aligned with + * the line box. + * + * @return ListBullet[] + */ + public function get_list_markers(): array + { + return $this->list_markers; + } + + /** + * Add a list marker to the line box. + * + * The list marker is only added for the purpose of vertical alignment, it + * is not actually added to the list of frames of the line box. + */ + public function add_list_marker(ListBullet $marker): void + { + $this->list_markers[] = $marker; + } + + /** + * An iterator of all list markers and inline positioned frames of the line + * box. + * + * @return \Iterator + */ + public function frames_to_align(): \Iterator + { + yield from $this->list_markers; + + foreach ($this->_frames as $frame) { + if ($frame->get_positioner() instanceof InlinePositioner) { + yield $frame; + } + } + } + + /** + * Trim trailing whitespace from the line. + */ + public function trim_trailing_ws(): void + { + $lastIndex = count($this->_frames) - 1; + + if ($lastIndex < 0) { + return; + } + + $lastFrame = $this->_frames[$lastIndex]; + $reflower = $lastFrame->get_reflower(); + + if ($reflower instanceof TextFrameReflower && !$lastFrame->is_pre()) { + $reflower->trim_trailing_ws(); + $this->recalculate_width(); + } } /** @@ -263,12 +376,12 @@ class LineBox * * @return float */ - public function recalculate_width() + public function recalculate_width(): float { - $width = 0; + $width = 0.0; - foreach ($this->get_frames() as $frame) { - $width += $frame->calculate_auto_width(); + foreach ($this->_frames as $frame) { + $width += $frame->get_margin_width(); } return $this->w = $width; @@ -277,9 +390,9 @@ class LineBox /** * @return string */ - public function __toString() + public function __toString(): string { - $props = array("wc", "y", "w", "h", "left", "right", "br"); + $props = ["wc", "y", "w", "h", "left", "right", "br"]; $s = ""; foreach ($props as $prop) { $s .= "$prop: " . $this->$prop . "\n"; @@ -288,10 +401,6 @@ class LineBox return $s; } - /*function __get($prop) { - if (!isset($this->{"_$prop"})) return; - return $this->{"_$prop"}; - }*/ } /* diff --git a/library/vendor/dompdf/src/Options.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Options.php similarity index 76% rename from library/vendor/dompdf/src/Options.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Options.php index b6c7c362c..de127d2d5 100644 --- a/library/vendor/dompdf/src/Options.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Options.php @@ -1,4 +1,9 @@ ["rules" => []], + "http://" => ["rules" => []], + "https://" => ["rules" => []] + ]; + /** * @var string */ private $logOutputFile; /** - * html target media view which should be rendered into pdf. - * List of types and parsing rules for future extensions: - * http://www.w3.org/TR/REC-html40/types.html - * screen, tty, tv, projection, handheld, print, braille, aural, all - * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3. - * Note, even though the generated pdf file is intended for print output, - * the desired content might be different (e.g. screen or projection view of html file). - * Therefore allow specification of content here. + * Styles targeted to this media type are applied to the document. + * This is on top of the media types that are always applied: + * all, static, visual, bitmap, paged, dompdf * * @var string */ @@ -182,28 +199,33 @@ class Options private $isRemoteEnabled = false; /** - * Enable inline Javascript + * Enable inline JavaScript * * If this setting is set to true then DOMPDF will automatically insert - * JavaScript code contained within tags. + * JavaScript code contained within + * tags as written into the PDF. + * + * NOTE: This is PDF-based JavaScript to be executed by the PDF viewer, + * not browser-based JavaScript executed by Dompdf. * * @var bool */ private $isJavascriptEnabled = true; /** - * Use the more-than-experimental HTML5 Lib parser + * Use the HTML5 Lib parser * + * @deprecated * @var bool */ - private $isHtml5ParserEnabled = false; + private $isHtml5ParserEnabled = true; /** * Whether to enable font subsetting or not. * * @var bool */ - private $isFontSubsettingEnabled = false; + private $isFontSubsettingEnabled = true; /** * @var bool @@ -275,28 +297,43 @@ class Options private $pdflibLicense = ""; /** - * @var string - * @deprecated + * HTTP context created with stream_context_create() + * Will be used for file_get_contents + * + * @link https://www.php.net/manual/context.php + * + * @var resource */ - private $adminUsername = "user"; - - /** - * @var string - * @deprecated - */ - private $adminPassword = "password"; + private $httpContext; /** * @param array $attributes */ public function __construct(array $attributes = null) { - $this->setChroot(realpath(__DIR__ . "/../")); - $this->setRootDir($this->getChroot()); + $rootDir = realpath(__DIR__ . "/../"); + $this->setChroot(array($rootDir)); + $this->setRootDir($rootDir); $this->setTempDir(sys_get_temp_dir()); - $this->setFontDir($this->chroot . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "fonts"); + $this->setFontDir($rootDir . "/lib/fonts"); $this->setFontCache($this->getFontDir()); - $this->setLogOutputFile($this->getTempDir() . DIRECTORY_SEPARATOR . "log.htm"); + + $ver = ""; + $versionFile = realpath(__DIR__ . '/../VERSION'); + if (($version = file_get_contents($versionFile)) !== false) { + $version = trim($version); + if ($version !== '$Format:<%h>$') { + $ver = "/$version"; + } + } + $this->setHttpContext([ + "http" => [ + "follow_location" => false, + "user_agent" => "Dompdf$ver https://github.com/dompdf/dompdf" + ] + ]); + + $this->setAllowedProtocols(["file://", "http://", "https://"]); if (null !== $attributes) { $this->set($attributes); @@ -311,7 +348,7 @@ class Options public function set($attributes, $value = null) { if (!is_array($attributes)) { - $attributes = array($attributes => $value); + $attributes = [$attributes => $value]; } foreach ($attributes as $key => $value) { if ($key === 'tempDir' || $key === 'temp_dir') { @@ -322,6 +359,8 @@ class Options $this->setFontCache($value); } elseif ($key === 'chroot') { $this->setChroot($value); + } elseif ($key === 'allowedProtocols') { + $this->setAllowedProtocols($value); } elseif ($key === 'logOutputFile' || $key === 'log_output_file') { $this->setLogOutputFile($value); } elseif ($key === 'defaultMediaType' || $key === 'default_media_type') { @@ -366,10 +405,8 @@ class Options $this->setPdfBackend($value); } elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') { $this->setPdflibLicense($value); - } elseif ($key === 'adminUsername' || $key === 'admin_username') { - $this->setAdminUsername($value); - } elseif ($key === 'adminPassword' || $key === 'admin_password') { - $this->setAdminPassword($value); + } elseif ($key === 'httpContext' || $key === 'http_context') { + $this->setHttpContext($value); } } return $this; @@ -389,6 +426,8 @@ class Options return $this->getFontCache(); } elseif ($key === 'chroot') { return $this->getChroot(); + } elseif ($key === 'allowedProtocols') { + return $this->getAllowedProtocols(); } elseif ($key === 'logOutputFile' || $key === 'log_output_file') { return $this->getLogOutputFile(); } elseif ($key === 'defaultMediaType' || $key === 'default_media_type') { @@ -433,50 +472,12 @@ class Options return $this->getPdfBackend(); } elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') { return $this->getPdflibLicense(); - } elseif ($key === 'adminUsername' || $key === 'admin_username') { - return $this->getAdminUsername(); - } elseif ($key === 'adminPassword' || $key === 'admin_password') { - return $this->getAdminPassword(); + } elseif ($key === 'httpContext' || $key === 'http_context') { + return $this->getHttpContext(); } return null; } - /** - * @param string $adminPassword - * @return $this - */ - public function setAdminPassword($adminPassword) - { - $this->adminPassword = $adminPassword; - return $this; - } - - /** - * @return string - */ - public function getAdminPassword() - { - return $this->adminPassword; - } - - /** - * @param string $adminUsername - * @return $this - */ - public function setAdminUsername($adminUsername) - { - $this->adminUsername = $adminUsername; - return $this; - } - - /** - * @return string - */ - public function getAdminUsername() - { - return $this->adminUsername; - } - /** * @param string $pdfBackend * @return $this @@ -514,21 +515,93 @@ class Options } /** - * @param string $chroot + * @param array|string $chroot * @return $this */ - public function setChroot($chroot) + public function setChroot($chroot, $delimiter = ',') { - $this->chroot = $chroot; + if (is_string($chroot)) { + $this->chroot = explode($delimiter, $chroot); + } elseif (is_array($chroot)) { + $this->chroot = $chroot; + } return $this; } /** - * @return string + * @return array + */ + public function getAllowedProtocols() + { + return $this->allowedProtocols; + } + + /** + * @param array $allowedProtocols The protocols to allow, as an array + * formatted as ["protocol://" => ["rules" => [callable]], ...] + * or ["protocol://", ...] + * + * @return $this + */ + public function setAllowedProtocols(array $allowedProtocols) + { + $protocols = []; + foreach ($allowedProtocols as $protocol => $config) { + if (is_string($protocol)) { + $protocols[$protocol] = []; + if (is_array($config)) { + $protocols[$protocol] = $config; + } + } elseif (is_string($config)) { + $protocols[$config] = []; + } + } + $this->allowedProtocols = []; + foreach ($protocols as $protocol => $config) { + $this->addAllowedProtocol($protocol, ...($config["rules"] ?? [])); + } + return $this; + } + + /** + * Adds a new protocol to the allowed protocols collection + * + * @param string $protocol The scheme to add (e.g. "http://") + * @param callable $rule A callable that validates the protocol + * @return $this + */ + public function addAllowedProtocol(string $protocol, callable ...$rules) + { + $protocol = strtolower($protocol); + if (empty($rules)) { + $rules = []; + switch ($protocol) { + case "file://": + $rules[] = [$this, "validateLocalUri"]; + break; + case "http://": + case "https://": + $rules[] = [$this, "validateRemoteUri"]; + break; + case "phar://": + $rules[] = [$this, "validatePharUri"]; + break; + } + } + $this->allowedProtocols[$protocol] = ["rules" => $rules]; + return $this; + } + + /** + * @return array */ public function getChroot() { - return $this->chroot; + $chroot = []; + if (is_array($this->chroot)) { + $chroot = $this->chroot; + } + return $chroot; } /** @@ -681,7 +754,11 @@ class Options */ public function setDefaultFont($defaultFont) { - $this->defaultFont = $defaultFont; + if (!($defaultFont === null || trim($defaultFont) === "")) { + $this->defaultFont = $defaultFont; + } else { + $this->defaultFont = "serif"; + } return $this; } @@ -846,6 +923,7 @@ class Options } /** + * @deprecated * @param boolean $isHtml5ParserEnabled * @return $this */ @@ -856,6 +934,7 @@ class Options } /** + * @deprecated * @return boolean */ public function getIsHtml5ParserEnabled() @@ -864,6 +943,7 @@ class Options } /** + * @deprecated * @return boolean */ public function isHtml5ParserEnabled() @@ -1002,4 +1082,78 @@ class Options { return $this->rootDir; } -} \ No newline at end of file + + /** + * Sets the HTTP context + * + * @param resource|array $httpContext + * @return $this + */ + public function setHttpContext($httpContext) + { + $this->httpContext = is_array($httpContext) ? stream_context_create($httpContext) : $httpContext; + return $this; + } + + /** + * Returns the HTTP context + * + * @return resource + */ + public function getHttpContext() + { + return $this->httpContext; + } + + public function validateLocalUri(string $uri) + { + if ($uri === null || strlen($uri) === 0) { + return [false, "The URI must not be empty."]; + } + + $realfile = realpath(str_replace("file://", "", $uri)); + + $dirs = $this->chroot; + $dirs[] = $this->rootDir; + $chrootValid = false; + foreach ($dirs as $chrootPath) { + $chrootPath = realpath($chrootPath); + if ($chrootPath !== false && strpos($realfile, $chrootPath) === 0) { + $chrootValid = true; + break; + } + } + if ($chrootValid !== true) { + return [false, "Permission denied. The file could not be found under the paths specified by Options::chroot."]; + } + + if (!$realfile) { + return [false, "File not found."]; + } + + return [true, null]; + } + + public function validatePharUri(string $uri) + { + if ($uri === null || strlen($uri) === 0) { + return [false, "The URI must not be empty."]; + } + + $file = substr(substr($uri, 0, strpos($uri, ".phar") + 5), 7); + return $this->validateLocalUri($file); + } + + public function validateRemoteUri(string $uri) + { + if ($uri === null || strlen($uri) === 0) { + return [false, "The URI must not be empty."]; + } + + if (!$this->isRemoteEnabled) { + return [false, "Remote file requested, but remote file download is disabled."]; + } + + return [true, null]; + } +} diff --git a/library/vendor/dompdf/src/PhpEvaluator.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/PhpEvaluator.php similarity index 89% rename from library/vendor/dompdf/src/PhpEvaluator.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/PhpEvaluator.php index 0058027a0..4a46555ca 100644 --- a/library/vendor/dompdf/src/PhpEvaluator.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/PhpEvaluator.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf; @@ -33,7 +32,7 @@ class PhpEvaluator * @param $code * @param array $vars */ - public function evaluate($code, $vars = array()) + public function evaluate($code, $vars = []) { if (!$this->_canvas->get_dompdf()->getOptions()->getIsPhpEnabled()) { return; diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Absolute.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Absolute.php new file mode 100644 index 000000000..2df9a742b --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Absolute.php @@ -0,0 +1,128 @@ +get_reflower() instanceof Block) { + $style = $frame->get_style(); + [$cbx, $cby, $cbw, $cbh] = $frame->get_containing_block(); + + // If the `top` value is `auto`, the frame will be repositioned + // after its height has been resolved + $left = (float) $style->length_in_pt($style->left, $cbw); + $top = (float) $style->length_in_pt($style->top, $cbh); + + $frame->set_position($cbx + $left, $cby + $top); + } else { + // Legacy positioning logic for image and table frames + // TODO: Resolve dimensions, margins, and offsets similar to the + // block case in the reflowers and use the simplified logic above + $style = $frame->get_style(); + $block_parent = $frame->find_block_parent(); + $current_line = $block_parent->get_current_line_box(); + + list($x, $y, $w, $h) = $frame->get_containing_block(); + $inflow_x = $block_parent->get_content_box()["x"] + $current_line->left + $current_line->w; + $inflow_y = $current_line->y; + + $top = $style->length_in_pt($style->top, $h); + $right = $style->length_in_pt($style->right, $w); + $bottom = $style->length_in_pt($style->bottom, $h); + $left = $style->length_in_pt($style->left, $w); + + list($width, $height) = [$frame->get_margin_width(), $frame->get_margin_height()]; + + $orig_width = $style->get_specified("width"); + $orig_height = $style->get_specified("height"); + + /**************************** + * + * Width auto: + * ____________| left=auto | left=fixed | + * right=auto | A | B | + * right=fixed | C | D | + * + * Width fixed: + * ____________| left=auto | left=fixed | + * right=auto | E | F | + * right=fixed | G | H | + *****************************/ + + if ($left === "auto") { + if ($right === "auto") { + // A or E - Keep the frame at the same position + $x = $inflow_x; + } else { + if ($orig_width === "auto") { + // C + $x += $w - $width - $right; + } else { + // G + $x += $w - $width - $right; + } + } + } else { + if ($right === "auto") { + // B or F + $x += (float)$left; + } else { + if ($orig_width === "auto") { + // D - TODO change width + $x += (float)$left; + } else { + // H - Everything is fixed: left + width win + $x += (float)$left; + } + } + } + + // The same vertically + if ($top === "auto") { + if ($bottom === "auto") { + // A or E - Keep the frame at the same position + $y = $inflow_y; + } else { + if ($orig_height === "auto") { + // C + $y += (float)$h - $height - (float)$bottom; + } else { + // G + $y += (float)$h - $height - (float)$bottom; + } + } + } else { + if ($bottom === "auto") { + // B or F + $y += (float)$top; + } else { + if ($orig_height === "auto") { + // D - TODO change height + $y += (float)$top; + } else { + // H - Everything is fixed: top + height win + $y += (float)$top; + } + } + } + + $frame->set_position($x, $y); + } + } +} diff --git a/library/vendor/dompdf/src/Positioner/AbstractPositioner.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/AbstractPositioner.php similarity index 57% rename from library/vendor/dompdf/src/Positioner/AbstractPositioner.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/AbstractPositioner.php index 2ade6afcb..a75c09fb9 100644 --- a/library/vendor/dompdf/src/Positioner/AbstractPositioner.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/AbstractPositioner.php @@ -1,11 +1,9 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\Positioner; use Dompdf\FrameDecorator\AbstractFrameDecorator; @@ -13,9 +11,8 @@ use Dompdf\FrameDecorator\AbstractFrameDecorator; /** * Base AbstractPositioner class * - * Defines postioner interface + * Defines positioner interface * - * @access private * @package dompdf */ abstract class AbstractPositioner @@ -23,19 +20,22 @@ abstract class AbstractPositioner /** * @param AbstractFrameDecorator $frame - * @return mixed */ - abstract function position(AbstractFrameDecorator $frame); + abstract function position(AbstractFrameDecorator $frame): void; /** * @param AbstractFrameDecorator $frame - * @param $offset_x - * @param $offset_y - * @param bool $ignore_self + * @param float $offset_x + * @param float $offset_y + * @param bool $ignore_self */ - function move(AbstractFrameDecorator $frame, $offset_x, $offset_y, $ignore_self = false) - { - list($x, $y) = $frame->get_position(); + function move( + AbstractFrameDecorator $frame, + float $offset_x, + float $offset_y, + bool $ignore_self = false + ): void { + [$x, $y] = $frame->get_position(); if (!$ignore_self) { $frame->set_position($x + $offset_x, $y + $offset_y); diff --git a/library/vendor/dompdf/src/Positioner/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Block.php similarity index 52% rename from library/vendor/dompdf/src/Positioner/Block.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Block.php index 0c340bc8c..e6f65eab9 100644 --- a/library/vendor/dompdf/src/Positioner/Block.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Block.php @@ -1,11 +1,9 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\Positioner; use Dompdf\FrameDecorator\AbstractFrameDecorator; @@ -13,12 +11,12 @@ use Dompdf\FrameDecorator\AbstractFrameDecorator; /** * Positions block frames * - * @access private * @package dompdf */ -class Block extends AbstractPositioner { +class Block extends AbstractPositioner +{ - function position(AbstractFrameDecorator $frame) + function position(AbstractFrameDecorator $frame): void { $style = $frame->get_style(); $cb = $frame->get_containing_block(); @@ -31,24 +29,12 @@ class Block extends AbstractPositioner { $p->add_line(true); } $y = $p->get_current_line_box()->y; - } else { $y = $cb["y"]; } $x = $cb["x"]; - // Relative positionning - if ($style->position === "relative") { - $top = (float)$style->length_in_pt($style->top, $cb["h"]); - //$right = (float)$style->length_in_pt($style->right, $cb["w"]); - //$bottom = (float)$style->length_in_pt($style->bottom, $cb["h"]); - $left = (float)$style->length_in_pt($style->left, $cb["w"]); - - $x += $left; - $y += $top; - } - $frame->set_position($x, $y); } } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Fixed.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Fixed.php new file mode 100644 index 000000000..13eb9e9c0 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Fixed.php @@ -0,0 +1,92 @@ +get_reflower() instanceof Block) { + parent::position($frame); + } else { + // Legacy positioning logic for image and table frames + // TODO: Resolve dimensions, margins, and offsets similar to the + // block case in the reflowers and use the simplified logic above + $style = $frame->get_style(); + $root = $frame->get_root(); + $initialcb = $root->get_containing_block(); + $initialcb_style = $root->get_style(); + + $p = $frame->find_block_parent(); + if ($p) { + $p->add_line(); + } + // Compute the margins of the @page style + $margin_top = (float)$initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]); + $margin_right = (float)$initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]); + $margin_bottom = (float)$initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]); + $margin_left = (float)$initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]); + + // The needed computed style of the element + $height = (float)$style->length_in_pt($style->get_specified("height"), $initialcb["h"]); + $width = (float)$style->length_in_pt($style->get_specified("width"), $initialcb["w"]); + + $top = $style->length_in_pt($style->get_specified("top"), $initialcb["h"]); + $right = $style->length_in_pt($style->get_specified("right"), $initialcb["w"]); + $bottom = $style->length_in_pt($style->get_specified("bottom"), $initialcb["h"]); + $left = $style->length_in_pt($style->get_specified("left"), $initialcb["w"]); + + $y = $margin_top; + if (isset($top)) { + $y = (float)$top + $margin_top; + if ($top === "auto") { + $y = $margin_top; + if (isset($bottom) && $bottom !== "auto") { + $y = $initialcb["h"] - $bottom - $margin_bottom; + if ($frame->is_auto_height()) { + $y -= $height; + } else { + $y -= $frame->get_margin_height(); + } + } + } + } + + $x = $margin_left; + if (isset($left)) { + $x = (float)$left + $margin_left; + if ($left === "auto") { + $x = $margin_left; + if (isset($right) && $right !== "auto") { + $x = $initialcb["w"] - $right - $margin_right; + if ($frame->is_auto_width()) { + $x -= $width; + } else { + $x -= $frame->get_margin_width(); + } + } + } + } + + $frame->set_position($x, $y); + + foreach ($frame->get_children() as $child) { + $child->set_position($x, $y); + } + } + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Inline.php new file mode 100644 index 000000000..c0bdaf263 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/Inline.php @@ -0,0 +1,52 @@ +find_block_parent(); + + if (!$block) { + throw new Exception("No block-level parent found. Not good."); + } + + $cb = $frame->get_containing_block(); + $line = $block->get_current_line_box(); + + if (!$frame->is_text_node() && !($frame instanceof InlineFrameDecorator)) { + // Atomic inline boxes and replaced inline elements + // (inline-block, inline-table, img etc.) + $width = $frame->get_margin_width(); + $available_width = $cb["w"] - $line->left - $line->w - $line->right; + + if (Helpers::lengthGreater($width, $available_width)) { + $block->add_line(); + $line = $block->get_current_line_box(); + } + } + + $frame->set_position($cb["x"] + $line->w, $line->y); + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/ListBullet.php new file mode 100644 index 000000000..081d59419 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/ListBullet.php @@ -0,0 +1,42 @@ +get_parent(); + $style = $parent->get_style(); + $cbw = $parent->get_containing_block("w"); + $margin_left = (float) $style->length_in_pt($style->margin_left, $cbw); + $border_edge = $parent->get_position("x") + $margin_left; + + // This includes the marker indentation + $x = $border_edge - $frame->get_margin_width(); + + // The marker is later vertically aligned with the corresponding line + // box and its vertical position is fine-tuned in the renderer + $p = $frame->find_block_parent(); + $y = $p->get_current_line_box()->y; + + $frame->set_position($x, $y); + } +} diff --git a/library/vendor/dompdf/src/Positioner/NullPositioner.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/NullPositioner.php similarity index 72% rename from library/vendor/dompdf/src/Positioner/NullPositioner.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/NullPositioner.php index afdef1901..6ad425c34 100644 --- a/library/vendor/dompdf/src/Positioner/NullPositioner.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/NullPositioner.php @@ -1,11 +1,9 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\Positioner; use Dompdf\FrameDecorator\AbstractFrameDecorator; @@ -21,7 +19,7 @@ class NullPositioner extends AbstractPositioner /** * @param AbstractFrameDecorator $frame */ - function position(AbstractFrameDecorator $frame) + function position(AbstractFrameDecorator $frame): void { return; } diff --git a/library/vendor/dompdf/src/Positioner/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableCell.php similarity index 79% rename from library/vendor/dompdf/src/Positioner/TableCell.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableCell.php index 42b0042e4..1a6ac6298 100644 --- a/library/vendor/dompdf/src/Positioner/TableCell.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableCell.php @@ -1,11 +1,9 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\Positioner; use Dompdf\FrameDecorator\AbstractFrameDecorator; @@ -22,7 +20,7 @@ class TableCell extends AbstractPositioner /** * @param AbstractFrameDecorator $frame */ - function position(AbstractFrameDecorator $frame) + function position(AbstractFrameDecorator $frame): void { $table = Table::find_parent_table($frame); $cellmap = $table->get_cellmap(); diff --git a/library/vendor/dompdf/src/Positioner/TableRow.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableRow.php similarity index 81% rename from library/vendor/dompdf/src/Positioner/TableRow.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableRow.php index aee20453f..79c0fcf25 100644 --- a/library/vendor/dompdf/src/Positioner/TableRow.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Positioner/TableRow.php @@ -1,11 +1,9 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ - namespace Dompdf\Positioner; use Dompdf\FrameDecorator\AbstractFrameDecorator; @@ -21,7 +19,7 @@ class TableRow extends AbstractPositioner /** * @param AbstractFrameDecorator $frame */ - function position(AbstractFrameDecorator $frame) + function position(AbstractFrameDecorator $frame): void { $cb = $frame->get_containing_block(); $p = $frame->get_prev_sibling(); diff --git a/library/vendor/dompdf/src/Renderer.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer.php similarity index 82% rename from library/vendor/dompdf/src/Renderer.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer.php index aa3bf2f88..e3cacc104 100644 --- a/library/vendor/dompdf/src/Renderer.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf; @@ -65,19 +64,21 @@ class Renderer extends AbstractRenderer $style = $frame->get_style(); - if (in_array($style->visibility, array("hidden", "collapse"))) { + if (in_array($style->visibility, ["hidden", "collapse"], true)) { return; } $display = $style->display; + $transformList = $style->transform; + $hasTransform = $transformList !== []; // Starts the CSS transformation - if ($style->transform && is_array($style->transform)) { + if ($hasTransform) { $this->_canvas->save(); list($x, $y) = $frame->get_padding_box(); $origin = $style->transform_origin; - foreach ($style->transform as $transform) { + foreach ($transformList as $transform) { list($function, $values) = $transform; if ($function === "matrix") { $function = "transform"; @@ -87,7 +88,7 @@ class Renderer extends AbstractRenderer $values[] = $x + (float)$style->length_in_pt($origin[0], (float)$style->length_in_pt($style->width)); $values[] = $y + (float)$style->length_in_pt($origin[1], (float)$style->length_in_pt($style->height)); - call_user_func_array(array($this->_canvas, $function), $values); + call_user_func_array([$this->_canvas, $function], $values); } } @@ -154,23 +155,23 @@ class Renderer extends AbstractRenderer // Starts the overflow: hidden box if ($style->overflow === "hidden") { - list($x, $y, $w, $h) = $frame->get_padding_box(); - - // get border radii + $padding_box = $frame->get_padding_box(); + [$x, $y, $w, $h] = $padding_box; $style = $frame->get_style(); - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - if ($tl + $tr + $br + $bl > 0) { - $this->_canvas->clipping_roundrectangle($x, $y, (float)$w, (float)$h, $tl, $tr, $br, $bl); + if ($style->has_border_radius()) { + $border_box = $frame->get_border_box(); + [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $padding_box); + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); } else { - $this->_canvas->clipping_rectangle($x, $y, (float)$w, (float)$h); + $this->_canvas->clipping_rectangle($x, $y, $w, $h); } } - $stack = array(); + $stack = []; foreach ($frame->get_children() as $child) { - // < 0 : nagative z-index + // < 0 : negative z-index // = 0 : no z-index, no stacking context // = 1 : stacking context without z-index // > 1 : z-index @@ -179,8 +180,8 @@ class Renderer extends AbstractRenderer $z_index = 0; if ($child_z_index !== "auto") { - $z_index = intval($child_z_index) + 1; - } elseif ($child_style->float !== "none" || $child->is_positionned()) { + $z_index = $child_z_index + 1; + } elseif ($child_style->float !== "none" || $child->is_positioned()) { $z_index = 1; } @@ -200,7 +201,7 @@ class Renderer extends AbstractRenderer $this->_canvas->clipping_end(); } - if ($style->transform && is_array($style->transform)) { + if ($hasTransform) { $this->_canvas->restore(); } @@ -212,27 +213,22 @@ class Renderer extends AbstractRenderer * Check for callbacks that need to be performed when a given event * gets triggered on a frame * - * @param string $event the type of event - * @param Frame $frame the frame that event is triggered on + * @param string $event The type of event + * @param Frame $frame The frame that event is triggered on */ - protected function _check_callbacks($event, $frame) + protected function _check_callbacks(string $event, Frame $frame): void { if (!isset($this->_callbacks)) { $this->_callbacks = $this->_dompdf->getCallbacks(); } - if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { - $info = array(0 => $this->_canvas, "canvas" => $this->_canvas, - 1 => $frame, "frame" => $frame); + if (isset($this->_callbacks[$event])) { $fs = $this->_callbacks[$event]; + $canvas = $this->_canvas; + $fontMetrics = $this->_dompdf->getFontMetrics(); + foreach ($fs as $f) { - if (is_callable($f)) { - if (is_array($f)) { - $f[0]->{$f[1]}($info); - } else { - $f($info); - } - } + $f($frame, $canvas, $fontMetrics); } } } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/AbstractRenderer.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/AbstractRenderer.php new file mode 100644 index 000000000..8b01ef8c0 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/AbstractRenderer.php @@ -0,0 +1,1244 @@ +_dompdf = $dompdf; + $this->_canvas = $dompdf->getCanvas(); + } + + /** + * Render a frame. + * + * Specialized in child classes + * + * @param Frame $frame The frame to render + */ + abstract function render(Frame $frame); + + /** + * @param Frame $frame + * @param float[] $border_box + */ + protected function _render_background(Frame $frame, array $border_box): void + { + $style = $frame->get_style(); + $color = $style->background_color; + $image = $style->background_image; + [$x, $y, $w, $h] = $border_box; + + if ($color === "transparent" && $image === "none") { + return; + } + + if ($style->has_border_radius()) { + [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box); + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + if ($color !== "transparent") { + $this->_canvas->filled_rectangle($x, $y, $w, $h, $color); + } + + if ($image !== "none") { + $this->_background_image($image, $x, $y, $w, $h, $style); + } + + if ($style->has_border_radius()) { + $this->_canvas->clipping_end(); + } + } + + /** + * @param Frame $frame + * @param float[] $border_box + * @param string $corner_style + */ + protected function _render_border(Frame $frame, array $border_box, string $corner_style = "bevel"): void + { + $style = $frame->get_style(); + $bp = $style->get_border_properties(); + [$x, $y, $w, $h] = $border_box; + [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box); + + // Short-cut: If all the borders are "solid" with the same color and + // style, and no radius, we'd better draw a rectangle + if ($bp["top"]["style"] === "solid" && + $bp["top"] === $bp["right"] && + $bp["right"] === $bp["bottom"] && + $bp["bottom"] === $bp["left"] && + !$style->has_border_radius() + ) { + $props = $bp["top"]; + if ($props["color"] === "transparent" || $props["width"] <= 0) { + return; + } + + $width = (float)$style->length_in_pt($props["width"]); + $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width); + return; + } + + // Do it the long way + $widths = [ + (float)$style->length_in_pt($bp["top"]["width"]), + (float)$style->length_in_pt($bp["right"]["width"]), + (float)$style->length_in_pt($bp["bottom"]["width"]), + (float)$style->length_in_pt($bp["left"]["width"]) + ]; + + foreach ($bp as $side => $props) { + if ($props["style"] === "none" || + $props["style"] === "hidden" || + $props["color"] === "transparent" || + $props["width"] <= 0 + ) { + continue; + } + + [$x, $y, $w, $h] = $border_box; + $method = "_border_" . $props["style"]; + + switch ($side) { + case "top": + $length = $w; + $r1 = $tl; + $r2 = $tr; + break; + + case "bottom": + $length = $w; + $y += $h; + $r1 = $bl; + $r2 = $br; + break; + + case "left": + $length = $h; + $r1 = $tl; + $r2 = $bl; + break; + + case "right": + $length = $h; + $x += $w; + $r1 = $tr; + $r2 = $br; + break; + + default: + break; + } + + // draw rounded corners + $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2); + } + } + + /** + * @param Frame $frame + * @param float[] $border_box + * @param string $corner_style + */ + protected function _render_outline(Frame $frame, array $border_box, string $corner_style = "bevel"): void + { + $style = $frame->get_style(); + + $width = $style->outline_width; + $outline_style = $style->outline_style; + $color = $style->outline_color; + + if ($outline_style === "none" || $color === "transparent" || $width <= 0) { + return; + } + + $offset = $style->outline_offset; + + [$x, $y, $w, $h] = $border_box; + $d = $width + $offset; + $outline_box = [$x - $d, $y - $d, $w + $d * 2, $h + $d * 2]; + [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $outline_box); + + $x -= $offset; + $y -= $offset; + $w += $offset * 2; + $h += $offset * 2; + + // For a simple outline, we can draw a rectangle + if ($outline_style === "solid" && !$style->has_border_radius()) { + $x -= $width / 2; + $y -= $width / 2; + $w += $width; + $h += $width; + + $this->_canvas->rectangle($x, $y, $w, $h, $color, $width); + return; + } + + $x -= $width; + $y -= $width; + $w += $width * 2; + $h += $width * 2; + + $method = "_border_" . $outline_style; + $widths = array_fill(0, 4, $width); + $sides = ["top", "right", "left", "bottom"]; + + foreach ($sides as $side) { + switch ($side) { + case "top": + $length = $w; + $side_x = $x; + $side_y = $y; + $r1 = $tl; + $r2 = $tr; + break; + + case "bottom": + $length = $w; + $side_x = $x; + $side_y = $y + $h; + $r1 = $bl; + $r2 = $br; + break; + + case "left": + $length = $h; + $side_x = $x; + $side_y = $y; + $r1 = $tl; + $r2 = $bl; + break; + + case "right": + $length = $h; + $side_x = $x + $w; + $side_y = $y; + $r1 = $tr; + $r2 = $br; + break; + + default: + break; + } + + $this->$method($side_x, $side_y, $length, $color, $widths, $side, $corner_style, $r1, $r2); + } + } + + /** + * Render a background image over a rectangular area + * + * @param string $url The background image to load + * @param float $x The left edge of the rectangular area + * @param float $y The top edge of the rectangular area + * @param float $width The width of the rectangular area + * @param float $height The height of the rectangular area + * @param Style $style The associated Style object + * + * @throws \Exception + */ + protected function _background_image($url, $x, $y, $width, $height, $style) + { + if (!function_exists("imagecreatetruecolor")) { + throw new \Exception("The PHP GD extension is required, but is not installed."); + } + + $sheet = $style->get_stylesheet(); + + // Skip degenerate cases + if ($width == 0 || $height == 0) { + return; + } + + $box_width = $width; + $box_height = $height; + + //debugpng + if ($this->_dompdf->getOptions()->getDebugPng()) { + print '[_background_image ' . $url . ']'; + } + + list($img, $type, /*$msg*/) = Cache::resolve_url( + $url, + $sheet->get_protocol(), + $sheet->get_host(), + $sheet->get_base_path(), + $this->_dompdf->getOptions() + ); + + // Bail if the image is no good + if (Cache::is_broken($img)) { + return; + } + + //Try to optimize away reading and composing of same background multiple times + //Postponing read with imagecreatefrom ...() + //final composition parameters and name not known yet + //Therefore read dimension directly from file, instead of creating gd object first. + //$img_w = imagesx($src); $img_h = imagesy($src); + + list($img_w, $img_h) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext()); + if ($img_w == 0 || $img_h == 0) { + return; + } + + // save for later check if file needs to be resized. + $org_img_w = $img_w; + $org_img_h = $img_h; + + $repeat = $style->background_repeat; + $dpi = $this->_dompdf->getOptions()->getDpi(); + + //Increase background resolution and dependent box size according to image resolution to be placed in + //Then image can be copied in without resize + $bg_width = round((float)($width * $dpi) / 72); + $bg_height = round((float)($height * $dpi) / 72); + + list($img_w, $img_h) = $this->_resize_background_image( + $img_w, + $img_h, + $bg_width, + $bg_height, + $style->background_size, + $dpi + ); + //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel + + list($bg_x, $bg_y) = $style->background_position; + + if (Helpers::is_percent($bg_x)) { + // The point $bg_x % from the left edge of the image is placed + // $bg_x % from the left edge of the background rectangle + $p = ((float)$bg_x) / 100.0; + $x1 = $p * $img_w; + $x2 = $p * $bg_width; + + $bg_x = $x2 - $x1; + } else { + $bg_x = (float)($style->length_in_pt($bg_x) * $dpi) / 72; + } + + $bg_x = round($bg_x + (float)$style->length_in_pt($style->border_left_width) * $dpi / 72); + + if (Helpers::is_percent($bg_y)) { + // The point $bg_y % from the left edge of the image is placed + // $bg_y % from the left edge of the background rectangle + $p = ((float)$bg_y) / 100.0; + $y1 = $p * $img_h; + $y2 = $p * $bg_height; + + $bg_y = $y2 - $y1; + } else { + $bg_y = (float)($style->length_in_pt($bg_y) * $dpi) / 72; + } + + $bg_y = round($bg_y + (float)$style->length_in_pt($style->border_top_width) * $dpi / 72); + + //clip background to the image area on partial repeat. Nothing to do if img off area + //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area + //On no repeat with positive offset: move size/start to have offset==0 + //Handle x/y Dimensions separately + + if ($repeat !== "repeat" && $repeat !== "repeat-x") { + //No repeat x + if ($bg_x < 0) { + $bg_width = $img_w + $bg_x; + } else { + $x += ($bg_x * 72) / $dpi; + $bg_width = $bg_width - $bg_x; + if ($bg_width > $img_w) { + $bg_width = $img_w; + } + $bg_x = 0; + } + + if ($bg_width <= 0) { + return; + } + + $width = (float)($bg_width * 72) / $dpi; + } else { + //repeat x + if ($bg_x < 0) { + $bg_x = -((-$bg_x) % $img_w); + } else { + $bg_x = $bg_x % $img_w; + if ($bg_x > 0) { + $bg_x -= $img_w; + } + } + } + + if ($repeat !== "repeat" && $repeat !== "repeat-y") { + //no repeat y + if ($bg_y < 0) { + $bg_height = $img_h + $bg_y; + } else { + $y += ($bg_y * 72) / $dpi; + $bg_height = $bg_height - $bg_y; + if ($bg_height > $img_h) { + $bg_height = $img_h; + } + $bg_y = 0; + } + if ($bg_height <= 0) { + return; + } + $height = (float)($bg_height * 72) / $dpi; + } else { + //repeat y + if ($bg_y < 0) { + $bg_y = -((-$bg_y) % $img_h); + } else { + $bg_y = $bg_y % $img_h; + if ($bg_y > 0) { + $bg_y -= $img_h; + } + } + } + + //Optimization, if repeat has no effect + if ($repeat === "repeat" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) { + $repeat = "repeat-x"; + } + + if ($repeat === "repeat" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) { + $repeat = "repeat-y"; + } + + if (($repeat === "repeat-x" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) || + ($repeat === "repeat-y" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) + ) { + $repeat = "no-repeat"; + } + + // Avoid rendering identical background-image variants multiple times + // This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color) + // Note: Here, bg_* are the start values, not end values after going through the tile loops! + + $key = implode("_", [$bg_width, $bg_height, $img_w, $img_h, $bg_x, $bg_y, $repeat]); + // FIXME: This will fail when a file with that exact name exists in the + // same directory, included in the document as regular image + $cpdfKey = $img . "_" . $key; + $tmpFile = Cache::getTempImage($img, $key); + $cached = ($this->_canvas instanceof CPDF && $this->_canvas->get_cpdf()->image_iscached($cpdfKey)) + || ($tmpFile !== null && file_exists($tmpFile)); + + if (!$cached) { + // img: image url string + // img_w, img_h: original image size in px + // width, height: box size in pt + // bg_width, bg_height: box size in px + // x, y: left/top edge of box on page in pt + // start_x, start_y: placement of image relative to pattern + // $repeat: repeat mode + // $bg: GD object of result image + // $src: GD object of original image + + // Create a new image to fit over the background rectangle + $bg = imagecreatetruecolor($bg_width, $bg_height); + $cpdfFromGd = true; + + switch (strtolower($type)) { + case "png": + $cpdfFromGd = false; + imagesavealpha($bg, true); + imagealphablending($bg, false); + $src = @imagecreatefrompng($img); + break; + + case "jpeg": + $src = @imagecreatefromjpeg($img); + break; + + case "webp": + $src = @imagecreatefromwebp($img); + break; + + case "gif": + $src = @imagecreatefromgif($img); + break; + + case "bmp": + $src = @Helpers::imagecreatefrombmp($img); + break; + + default: + return; // Unsupported image type + } + + if ($src == null) { + return; + } + + if ($img_w != $org_img_w || $img_h != $org_img_h) { + $newSrc = imagescale($src, $img_w, $img_h); + imagedestroy($src); + $src = $newSrc; + } + + if ($src == null) { + return; + } + + //Background color if box is not relevant here + //Non transparent image: box clipped to real size. Background non relevant. + //Transparent image: The image controls the transparency and lets shine through whatever background. + //However on transparent image preset the composed image with the transparency color, + //to keep the transparency when copying over the non transparent parts of the tiles. + $ti = imagecolortransparent($src); + $palletsize = imagecolorstotal($src); + + if ($ti >= 0 && $ti < $palletsize) { + $tc = imagecolorsforindex($src, $ti); + $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']); + imagefill($bg, 0, 0, $ti); + imagecolortransparent($bg, $ti); + } + + //This has only an effect for the non repeatable dimension. + //compute start of src and dest coordinates of the single copy + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + } else { + $src_x = 0; + $dst_x = $bg_x; + } + + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + } else { + $src_y = 0; + $dst_y = $bg_y; + } + + //For historical reasons exchange meanings of variables: + //start_* will be the start values, while bg_* will be the temporary start values in the loops + $start_x = $bg_x; + $start_y = $bg_y; + + // Copy regions from the source image to the background + if ($repeat === "no-repeat") { + // Simply place the image on the background + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h); + + } elseif ($repeat === "repeat-x") { + for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + $w = $img_w + $bg_x; + } else { + $dst_x = $bg_x; + $src_x = 0; + $w = $img_w; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h); + } + } elseif ($repeat === "repeat-y") { + + for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + $h = $img_h + $bg_y; + } else { + $dst_y = $bg_y; + $src_y = 0; + $h = $img_h; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h); + } + } elseif ($repeat === "repeat") { + for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { + for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + $w = $img_w + $bg_x; + } else { + $dst_x = $bg_x; + $src_x = 0; + $w = $img_w; + } + + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + $h = $img_h + $bg_y; + } else { + $dst_y = $bg_y; + $src_y = 0; + $h = $img_h; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h); + } + } + } else { + print 'Unknown repeat!'; + } + + imagedestroy($src); + + if ($cpdfFromGd && $this->_canvas instanceof CPDF) { + // Skip writing temp file as the GD object is added directly + } else { + $tmpDir = $this->_dompdf->getOptions()->getTempDir(); + $tmpName = @tempnam($tmpDir, "bg_dompdf_img_"); + @unlink($tmpName); + $tmpFile = "$tmpName.png"; + + imagepng($bg, $tmpFile); + imagedestroy($bg); + + Cache::addTempImage($img, $tmpFile, $key); + } + } else { + $bg = null; + $cpdfFromGd = $tmpFile === null; + } + + if ($this->_dompdf->getOptions()->getDebugPng()) { + print '[_background_image ' . $tmpFile . ']'; + } + + $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height); + + // When using cpdf and optimization to direct png creation from gd object is available, + // don't create temp file, but place gd object directly into the pdf + if ($cpdfFromGd && $this->_canvas instanceof CPDF) { + // Note: CPDF_Adapter image converts y position + $this->_canvas->get_cpdf()->addImagePng($bg, $cpdfKey, $x, $this->_canvas->get_height() - $y - $height, $width, $height); + + if (isset($bg)) { + imagedestroy($bg); + } + } else { + $this->_canvas->image($tmpFile, $x, $y, $width, $height); + } + + $this->_canvas->clipping_end(); + } + + // Border rendering functions + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2); + } + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2); + } + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2); + } + + /** + * @param string $side + * @param float $ratio + * @param float $top + * @param float $right + * @param float $bottom + * @param float $left + * @param float $x + * @param float $y + * @param float $length + * @param float $r1 + * @param float $r2 + */ + protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) + { + switch ($side) { + case "top": + $r1 -= $left * $ratio; + $r2 -= $right * $ratio; + $x += $left * $ratio; + $y += $top * $ratio; + $length -= $left * $ratio + $right * $ratio; + break; + + case "bottom": + $r1 -= $right * $ratio; + $r2 -= $left * $ratio; + $x += $left * $ratio; + $y -= $bottom * $ratio; + $length -= $left * $ratio + $right * $ratio; + break; + + case "left": + $r1 -= $top * $ratio; + $r2 -= $bottom * $ratio; + $x += $left * $ratio; + $y += $top * $ratio; + $length -= $top * $ratio + $bottom * $ratio; + break; + + case "right": + $r1 -= $bottom * $ratio; + $r2 -= $top * $ratio; + $x -= $right * $ratio; + $y += $top * $ratio; + $length -= $top * $ratio + $bottom * $ratio; + break; + + default: + return; + } + } + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $third_widths = [$top / 3, $right / 3, $bottom / 3, $left / 3]; + + // draw the outer border + $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 2 / 3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); + } + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $half_widths = [$top / 2, $right / 2, $bottom / 2, $left / 2]; + + $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + } + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $half_widths = [$top / 2, $right / 2, $bottom / 2, $left / 2]; + + $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + } + + /** + * @param $c + * @return mixed + */ + protected function _tint($c) + { + if (!is_numeric($c)) { + return $c; + } + + return min(1, $c + 0.16); + } + + /** + * @param $c + * @return mixed + */ + protected function _shade($c) + { + if (!is_numeric($c)) { + return $c; + } + + return max(0, $c - 0.33); + } + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + switch ($side) { + case "top": + case "left": + $shade = array_map([$this, "_shade"], $color); + $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); + break; + + case "bottom": + case "right": + $tint = array_map([$this, "_tint"], $color); + $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); + break; + + default: + return; + } + } + + /** + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param float $r1 + * @param float $r2 + */ + protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + switch ($side) { + case "top": + case "left": + $tint = array_map([$this, "_tint"], $color); + $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); + break; + + case "bottom": + case "right": + $shade = array_map([$this, "_shade"], $color); + $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); + break; + + default: + return; + } + } + + /** + * Get the dash pattern and cap style for the given border style, width, and + * line length. + * + * The base pattern is adjusted so that it fits the given line length + * symmetrically. + * + * @param string $style + * @param float $width + * @param float $length + * + * @return array + */ + protected function dashPattern(string $style, float $width, float $length): array + { + if ($style === "dashed") { + $w = 3 * $width; + + if ($length < $w) { + $s = $w; + } else { + // Scale dashes and gaps + $r = round($length / $w); + $r = $r % 2 === 0 ? $r + 1 : $r; + $s = $length / $r; + } + + return [[$s], "butt"]; + } + + if ($style === "dotted") { + // Draw circles along the line + // Round caps extend outwards by half line width, so a zero dash + // width results in a circle + $gap = $width <= 1 ? 2 : 1; + $w = ($gap + 1) * $width; + + if ($length < $w) { + $s = $w; + } else { + // Only scale gaps + $l = $length - $width; + $r = max(round($l / $w), 1); + $s = $l / $r; + } + + return [[0, $s], "round"]; + } + + return [[], "butt"]; + } + + /** + * Draws a solid, dotted, or dashed line, observing the border radius + * + * @param float $x + * @param float $y + * @param float $length + * @param array $color + * @param float[] $widths + * @param string $side + * @param string $corner_style + * @param string $pattern_name + * @param float $r1 + * @param float $r2 + */ + protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name = "none", $r1 = 0, $r2 = 0) + { + /** used by $$side */ + [$top, $right, $bottom, $left] = $widths; + $width = $$side; + + // No need to clip corners if border radius is large enough + $cornerClip = $corner_style === "bevel" && ($r1 < $width || $r2 < $width); + $lineLength = $length - $r1 - $r2; + [$pattern, $cap] = $this->dashPattern($pattern_name, $width, $lineLength); + + // Determine arc border radius for corner arcs + $halfWidth = $width / 2; + $ar1 = max($r1 - $halfWidth, 0); + $ar2 = max($r2 - $halfWidth, 0); + + // Small angle adjustments to prevent the background from shining through + $adj1 = $ar1 / 80; + $adj2 = $ar2 / 80; + + // Adjust line width and corner angles to account for the fact that + // round caps extend outwards. The line is actually only shifted below, + // not shortened, as otherwise the end dash (circle) will vanish + // occasionally + $dl = $cap === "round" ? $halfWidth : 0; + + if ($cap === "round" && $ar1 > 0) { + $adj1 -= rad2deg(asin($halfWidth / $ar1)); + } + if ($cap === "round" && $ar2 > 0) { + $adj2 -= rad2deg(asin($halfWidth / $ar2)); + } + + switch ($side) { + case "top": + if ($cornerClip) { + $points = [ + $x, $y, + $x, $y - 1, // Extend outwards to avoid gaps + $x + $length, $y - 1, // Extend outwards to avoid gaps + $x + $length, $y, + $x + $length - max($right, $r2), $y + max($width, $r2), + $x + max($left, $r1), $y + max($width, $r1) + ]; + $this->_canvas->clipping_polygon($points); + } + + $y += $halfWidth; + + if ($ar1 > 0 && $adj1 > -22.5) { + $this->_canvas->arc($x + $r1, $y + $ar1, $ar1, $ar1, 90 - $adj1, 135 + $adj1, $color, $width, $pattern, $cap); + } + + if ($lineLength > 0) { + $this->_canvas->line($x + $dl + $r1, $y, $x + $dl + $length - $r2, $y, $color, $width, $pattern, $cap); + } + + if ($ar2 > 0 && $adj2 > -22.5) { + $this->_canvas->arc($x + $length - $r2, $y + $ar2, $ar2, $ar2, 45 - $adj2, 90 + $adj2, $color, $width, $pattern, $cap); + } + break; + + case "bottom": + if ($cornerClip) { + $points = [ + $x, $y, + $x, $y + 1, // Extend outwards to avoid gaps + $x + $length, $y + 1, // Extend outwards to avoid gaps + $x + $length, $y, + $x + $length - max($right, $r2), $y - max($width, $r2), + $x + max($left, $r1), $y - max($width, $r1) + ]; + $this->_canvas->clipping_polygon($points); + } + + $y -= $halfWidth; + + if ($ar1 > 0 && $adj1 > -22.5) { + $this->_canvas->arc($x + $r1, $y - $ar1, $ar1, $ar1, 225 - $adj1, 270 + $adj1, $color, $width, $pattern, $cap); + } + + if ($lineLength > 0) { + $this->_canvas->line($x + $dl + $r1, $y, $x + $dl + $length - $r2, $y, $color, $width, $pattern, $cap); + } + + if ($ar2 > 0 && $adj2 > -22.5) { + $this->_canvas->arc($x + $length - $r2, $y - $ar2, $ar2, $ar2, 270 - $adj2, 315 + $adj2, $color, $width, $pattern, $cap); + } + break; + + case "left": + if ($cornerClip) { + $points = [ + $x, $y, + $x - 1, $y, // Extend outwards to avoid gaps + $x - 1, $y + $length, // Extend outwards to avoid gaps + $x, $y + $length, + $x + max($width, $r2), $y + $length - max($bottom, $r2), + $x + max($width, $r1), $y + max($top, $r1) + ]; + $this->_canvas->clipping_polygon($points); + } + + $x += $halfWidth; + + if ($ar1 > 0 && $adj1 > -22.5) { + $this->_canvas->arc($x + $ar1, $y + $r1, $ar1, $ar1, 135 - $adj1, 180 + $adj1, $color, $width, $pattern, $cap); + } + + if ($lineLength > 0) { + $this->_canvas->line($x, $y + $dl + $r1, $x, $y + $dl + $length - $r2, $color, $width, $pattern, $cap); + } + + if ($ar2 > 0 && $adj2 > -22.5) { + $this->_canvas->arc($x + $ar2, $y + $length - $r2, $ar2, $ar2, 180 - $adj2, 225 + $adj2, $color, $width, $pattern, $cap); + } + break; + + case "right": + if ($cornerClip) { + $points = [ + $x, $y, + $x + 1, $y, // Extend outwards to avoid gaps + $x + 1, $y + $length, // Extend outwards to avoid gaps + $x, $y + $length, + $x - max($width, $r2), $y + $length - max($bottom, $r2), + $x - max($width, $r1), $y + max($top, $r1) + ]; + $this->_canvas->clipping_polygon($points); + } + + $x -= $halfWidth; + + if ($ar1 > 0 && $adj1 > -22.5) { + $this->_canvas->arc($x - $ar1, $y + $r1, $ar1, $ar1, 0 - $adj1, 45 + $adj1, $color, $width, $pattern, $cap); + } + + if ($lineLength > 0) { + $this->_canvas->line($x, $y + $dl + $r1, $x, $y + $dl + $length - $r2, $color, $width, $pattern, $cap); + } + + if ($ar2 > 0 && $adj2 > -22.5) { + $this->_canvas->arc($x - $ar2, $y + $length - $r2, $ar2, $ar2, 315 - $adj2, 360 + $adj2, $color, $width, $pattern, $cap); + } + break; + } + + if ($cornerClip) { + $this->_canvas->clipping_end(); + } + } + + /** + * @param float $opacity + */ + protected function _set_opacity(float $opacity): void + { + if ($opacity >= 0.0 && $opacity <= 1.0) { + $this->_canvas->set_opacity($opacity); + } + } + + /** + * @param float[] $box + * @param string $color + * @param array $style + */ + protected function _debug_layout($box, $color = "red", $style = []) + { + $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], Color::parse($color), 0.1, $style); + } + + /** + * @param float $img_width + * @param float $img_height + * @param float $container_width + * @param float $container_height + * @param array|string $bg_resize + * @param int $dpi + * + * @return array + */ + protected function _resize_background_image( + $img_width, + $img_height, + $container_width, + $container_height, + $bg_resize, + $dpi + ) { + // We got two some specific numbers and/or auto definitions + if (is_array($bg_resize)) { + $is_auto_width = $bg_resize[0] === 'auto'; + if ($is_auto_width) { + $new_img_width = $img_width; + } else { + $new_img_width = $bg_resize[0]; + if (Helpers::is_percent($new_img_width)) { + $new_img_width = round(($container_width / 100) * (float)$new_img_width); + } else { + $new_img_width = round($new_img_width * $dpi / 72); + } + } + + $is_auto_height = $bg_resize[1] === 'auto'; + if ($is_auto_height) { + $new_img_height = $img_height; + } else { + $new_img_height = $bg_resize[1]; + if (Helpers::is_percent($new_img_height)) { + $new_img_height = round(($container_height / 100) * (float)$new_img_height); + } else { + $new_img_height = round($new_img_height * $dpi / 72); + } + } + + // if one of both was set to auto the other one needs to scale proportionally + if ($is_auto_width !== $is_auto_height) { + if ($is_auto_height) { + $new_img_height = round($new_img_width * ($img_height / $img_width)); + } else { + $new_img_width = round($new_img_height * ($img_width / $img_height)); + } + } + } else { + $container_ratio = $container_height / $container_width; + + if ($bg_resize === 'cover' || $bg_resize === 'contain') { + $img_ratio = $img_height / $img_width; + + if ( + ($bg_resize === 'cover' && $container_ratio > $img_ratio) || + ($bg_resize === 'contain' && $container_ratio < $img_ratio) + ) { + $new_img_height = $container_height; + $new_img_width = round($container_height / $img_ratio); + } else { + $new_img_width = $container_width; + $new_img_height = round($container_width * $img_ratio); + } + } else { + $new_img_width = $img_width; + $new_img_height = $img_height; + } + } + + return [$new_img_width, $new_img_height]; + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Block.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Block.php new file mode 100644 index 000000000..99db1929a --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Block.php @@ -0,0 +1,88 @@ +get_style(); + $node = $frame->get_node(); + $dompdf = $this->_dompdf; + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + [$x, $y, $w, $h] = $frame->get_border_box(); + + if ($node->nodeName === "body") { + // Margins should be fully resolved at this point + $mt = $style->margin_top; + $mb = $style->margin_bottom; + $h = $frame->get_containing_block("h") - $mt - $mb; + } + + $border_box = [$x, $y, $w, $h]; + + // Draw our background, border and content + $this->_render_background($frame, $border_box); + $this->_render_border($frame, $border_box); + $this->_render_outline($frame, $border_box); + + // Handle anchors & links + if ($node->nodeName === "a" && $href = $node->getAttribute("href")) { + $href = Helpers::build_url($dompdf->getProtocol(), $dompdf->getBaseHost(), $dompdf->getBasePath(), $href) ?? $href; + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + + $id = $frame->get_node()->getAttribute("id"); + if (strlen($id) > 0) { + $this->_canvas->add_named_dest($id); + } + + $this->debugBlockLayout($frame, "red", false); + } + + protected function debugBlockLayout(Frame $frame, ?string $color, bool $lines = false): void + { + $options = $this->_dompdf->getOptions(); + $debugLayout = $options->getDebugLayout(); + + if (!$debugLayout) { + return; + } + + if ($color && $options->getDebugLayoutBlocks()) { + $this->_debug_layout($frame->get_border_box(), $color); + + if ($options->getDebugLayoutPaddingBox()) { + $this->_debug_layout($frame->get_padding_box(), $color, [0.5, 0.5]); + } + } + + if ($lines && $options->getDebugLayoutLines() && $frame instanceof BlockFrameDecorator) { + [$cx, , $cw] = $frame->get_content_box(); + + foreach ($frame->get_line_boxes() as $line) { + $lw = $cw - $line->left - $line->right; + $this->_debug_layout([$cx + $line->left, $line->y, $lw, $line->h], "orange"); + } + } + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Image.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Image.php new file mode 100644 index 000000000..61f684f94 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Image.php @@ -0,0 +1,90 @@ +get_style(); + $border_box = $frame->get_border_box(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + // Render background & borders + $this->_render_background($frame, $border_box); + $this->_render_border($frame, $border_box); + $this->_render_outline($frame, $border_box); + + $content_box = $frame->get_content_box(); + [$x, $y, $w, $h] = $content_box; + + $src = $frame->get_image_url(); + $alt = null; + + if (Cache::is_broken($src) && + $alt = $frame->get_node()->getAttribute("alt") + ) { + $font = $style->font_family; + $size = $style->font_size; + $word_spacing = $style->word_spacing; + $letter_spacing = $style->letter_spacing; + + $this->_canvas->text( + $x, + $y, + $alt, + $font, + $size, + $style->color, + $word_spacing, + $letter_spacing + ); + } elseif ($w > 0 && $h > 0) { + if ($style->has_border_radius()) { + [$tl, $tr, $br, $bl] = $style->resolve_border_radius($border_box, $content_box); + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + $this->_canvas->image($src, $x, $y, $w, $h, $style->image_resolution); + + if ($style->has_border_radius()) { + $this->_canvas->clipping_end(); + } + } + + if ($msg = $frame->get_image_msg()) { + $parts = preg_split("/\s*\n\s*/", $msg); + $font = $style->font_family; + $height = 10; + $_y = $alt ? $y + $h - count($parts) * $height : $y; + + foreach ($parts as $i => $_part) { + $this->_canvas->text($x, $_y + $i * $height, $_part, $font, $height * 0.8, [0.5, 0.5, 0.5]); + } + } + + $id = $frame->get_node()->getAttribute("id"); + if (strlen($id) > 0) { + $this->_canvas->add_named_dest($id); + } + + $this->debugBlockLayout($frame, "blue"); + } +} diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Inline.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Inline.php new file mode 100644 index 000000000..ad3546492 --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Inline.php @@ -0,0 +1,126 @@ +get_first_child()) { + return; // No children, no service + } + + $style = $frame->get_style(); + $dompdf = $this->_dompdf; + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $do_debug_layout_line = $dompdf->getOptions()->getDebugLayout() + && $dompdf->getOptions()->getDebugLayoutInline(); + + // Draw the background & border behind each child. To do this we need + // to figure out just how much space each child takes: + [$x, $y] = $frame->get_first_child()->get_position(); + [$w, $h] = $this->get_child_size($frame, $do_debug_layout_line); + + [, , $cbw] = $frame->get_containing_block(); + $margin_left = $style->length_in_pt($style->margin_left, $cbw); + $pt = $style->length_in_pt($style->padding_top, $cbw); + $pb = $style->length_in_pt($style->padding_bottom, $cbw); + + // Make sure that border and background start inside the left margin + // Extend the drawn box by border and padding in vertical direction, as + // these do not affect layout + // FIXME: Using a small vertical offset of a fraction of the height here + // to work around the vertical position being slightly off in general + $x += $margin_left; + $y -= $style->border_top_width + $pt - ($h * 0.1); + $w += $style->border_left_width + $style->border_right_width; + $h += $style->border_top_width + $pt + $style->border_bottom_width + $pb; + + $border_box = [$x, $y, $w, $h]; + $this->_render_background($frame, $border_box); + $this->_render_border($frame, $border_box); + $this->_render_outline($frame, $border_box); + + $node = $frame->get_node(); + $id = $node->getAttribute("id"); + if (strlen($id) > 0) { + $this->_canvas->add_named_dest($id); + } + + // Only two levels of links frames + $is_link_node = $node->nodeName === "a"; + if ($is_link_node) { + if (($name = $node->getAttribute("name"))) { + $this->_canvas->add_named_dest($name); + } + } + + if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") { + $link_node = $frame->get_parent()->get_node(); + } + + // Handle anchors & links + if ($is_link_node) { + if ($href = $node->getAttribute("href")) { + $href = Helpers::build_url($dompdf->getProtocol(), $dompdf->getBaseHost(), $dompdf->getBasePath(), $href) ?? $href; + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + } + } + + protected function get_child_size(Frame $frame, bool $do_debug_layout_line): array + { + $w = 0.0; + $h = 0.0; + + foreach ($frame->get_children() as $child) { + if ($child->get_node()->nodeValue === " " && $child->get_prev_sibling() && !$child->get_next_sibling()) { + break; + } + + $style = $child->get_style(); + $auto_width = $style->width === "auto"; + $auto_height = $style->height === "auto"; + [, , $child_w, $child_h] = $child->get_padding_box(); + + if ($auto_width || $auto_height) { + [$child_w2, $child_h2] = $this->get_child_size($child, $do_debug_layout_line); + + if ($auto_width) { + $child_w = $child_w2; + } + + if ($auto_height) { + $child_h = $child_h2; + } + } + + $w += $child_w; + $h = max($h, $child_h); + + if ($do_debug_layout_line) { + $this->_debug_layout($child->get_border_box(), "blue"); + + if ($this->_dompdf->getOptions()->getDebugLayoutPaddingBox()) { + $this->_debug_layout($child->get_padding_box(), "blue", [0.5, 0.5]); + } + } + } + + return [$w, $h]; + } +} diff --git a/library/vendor/dompdf/src/Renderer/ListBullet.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/ListBullet.php similarity index 63% rename from library/vendor/dompdf/src/Renderer/ListBullet.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/ListBullet.php index fa0bc3731..2df6696ad 100644 --- a/library/vendor/dompdf/src/Renderer/ListBullet.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/ListBullet.php @@ -1,22 +1,20 @@ - * @author Helmut Tischer + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Renderer; use Dompdf\Helpers; use Dompdf\Frame; -use Dompdf\Image\Cache; use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator; +use Dompdf\FrameDecorator\ListBulletImage; +use Dompdf\Image\Cache; /** * Renders list bullets * - * @access private * @package dompdf */ class ListBullet extends AbstractRenderer @@ -27,7 +25,7 @@ class ListBullet extends AbstractRenderer */ static function get_counter_chars($type) { - static $cache = array(); + static $cache = []; if (isset($cache[$type])) { return $cache[$type]; @@ -75,9 +73,9 @@ class ListBullet extends AbstractRenderer } /** - * @param integer $n + * @param int $n * @param string $type - * @param integer $pad + * @param int|null $pad * * @return string */ @@ -105,7 +103,7 @@ class ListBullet extends AbstractRenderer case "lower-alpha": case "lower-latin": case "a": - $text = chr(($n % 26) + ord('a') - 1); + $text = chr((($n - 1) % 26) + ord('a')); break; case "upper-roman": @@ -129,68 +127,54 @@ class ListBullet extends AbstractRenderer } /** - * @param Frame $frame + * @param ListBulletFrameDecorator $frame */ function render(Frame $frame) { + $li = $frame->get_parent(); $style = $frame->get_style(); - $font_size = $style->font_size; - $line_height = (float)$style->length_in_pt($style->line_height, $frame->get_containing_block("h")); $this->_set_opacity($frame->get_opacity($style->opacity)); - $li = $frame->get_parent(); - - // Don't render bullets twice if if was split - if ($li->_splitted) { + // Don't render bullets twice if the list item was split + if ($li->is_split_off) { return; } + $font_family = $style->font_family; + $font_size = $style->font_size; + $baseline = $this->_canvas->get_font_baseline($font_family, $font_size); + // Handle list-style-image // If list style image is requested but missing, fall back to predefined types - if ($style->list_style_image !== "none" && !Cache::is_broken($img = $frame->get_image_url())) { - list($x, $y) = $frame->get_position(); - - //For expected size and aspect, instead of box size, use image natural size scaled to DPI. - // Resample the bullet image to be consistent with 'auto' sized images - // See also Image::get_min_max_width - // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. - //$w = $frame->get_width(); - //$h = $frame->get_height(); - list($width, $height) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext()); - $dpi = $this->_dompdf->getOptions()->getDpi(); - $w = ((float)rtrim($width, "px") * 72) / $dpi; - $h = ((float)rtrim($height, "px") * 72) / $dpi; - - $x -= $w; - $y -= ($line_height - $font_size) / 2; //Reverse hinting of list_bullet_positioner + if ($frame instanceof ListBulletImage && !Cache::is_broken($img = $frame->get_image_url())) { + [$x, $y] = $frame->get_position(); + $w = $frame->get_width(); + $h = $frame->get_height(); + $y += $baseline - $h; $this->_canvas->image($img, $x, $y, $w, $h); } else { $bullet_style = $style->list_style_type; - $fill = false; - switch ($bullet_style) { default: - /** @noinspection PhpMissingBreakStatementInspection */ case "disc": - $fill = true; - case "circle": - list($x, $y) = $frame->get_position(); - $r = ($font_size * (ListBulletFrameDecorator::BULLET_SIZE /*-ListBulletFrameDecorator::BULLET_THICKNESS*/)) / 2; - $x -= $font_size * (ListBulletFrameDecorator::BULLET_SIZE / 2); - $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT)) / 2; + [$x, $y] = $frame->get_position(); + $offset = $font_size * ListBulletFrameDecorator::BULLET_OFFSET; + $r = ($font_size * ListBulletFrameDecorator::BULLET_SIZE) / 2; + $x += $r; + $y += $baseline - $r - $offset; $o = $font_size * ListBulletFrameDecorator::BULLET_THICKNESS; - $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill); + $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $bullet_style !== "circle"); break; case "square": - list($x, $y) = $frame->get_position(); + [$x, $y] = $frame->get_position(); + $offset = $font_size * ListBulletFrameDecorator::BULLET_OFFSET; $w = $font_size * ListBulletFrameDecorator::BULLET_SIZE; - $x -= $w; - $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT - ListBulletFrameDecorator::BULLET_SIZE)) / 2; + $y += $baseline - $w - $offset; $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color); break; @@ -222,25 +206,21 @@ class ListBullet extends AbstractRenderer $index = $node->getAttribute("dompdf-counter"); $text = $this->make_counter($index, $bullet_style, $pad); - if (trim($text) == "") { + if (trim($text) === "") { return; } - $spacing = 0; - $font_family = $style->font_family; + $word_spacing = $style->word_spacing; + $letter_spacing = $style->letter_spacing; + $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font_family, $font_size, $word_spacing, $letter_spacing); - $line = $li->get_containing_line(); - list($x, $y) = array($frame->get_position("x"), $line->y); - - $x -= $this->_dompdf->getFontMetrics()->getTextWidth($text, $font_family, $font_size, $spacing); - - // Take line-height into account - $line_height = $style->line_height; - $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results + [$x, $y] = $frame->get_position(); + // Correct for static frame width applied by positioner + $x += $frame->get_width() - $text_width; $this->_canvas->text($x, $y, $text, $font_family, $font_size, - $style->color, $spacing); + $style->color, $word_spacing, $letter_spacing); case "none": break; @@ -248,7 +228,7 @@ class ListBullet extends AbstractRenderer } $id = $frame->get_node()->getAttribute("id"); - if (strlen($id) > 0) { + if (strlen($id) > 0) { $this->_canvas->add_named_dest($id); } } diff --git a/library/vendor/dompdf/src/Renderer/TableCell.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableCell.php similarity index 66% rename from library/vendor/dompdf/src/Renderer/TableCell.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableCell.php index 8e607927c..cbbffd34c 100644 --- a/library/vendor/dompdf/src/Renderer/TableCell.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableCell.php @@ -1,8 +1,7 @@ + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Renderer; @@ -30,38 +29,56 @@ class TableCell extends Block } $this->_set_opacity($frame->get_opacity($style->opacity)); - list($x, $y, $w, $h) = $frame->get_border_box(); - - // Draw our background, border and content - if (($bg = $style->background_color) !== "transparent") { - $this->_canvas->filled_rectangle($x, $y, (float)$w, (float)$h, $bg); - } - - if (($url = $style->background_image) && $url !== "none") { - $this->_background_image($url, $x, $y, $w, $h, $style); - } + $border_box = $frame->get_border_box(); $table = Table::find_parent_table($frame); if ($table->get_style()->border_collapse !== "collapse") { - $this->_render_border($frame); - $this->_render_outline($frame); - return; + $this->_render_background($frame, $border_box); + $this->_render_border($frame, $border_box); + $this->_render_outline($frame, $border_box); + } else { + // The collapsed case is slightly complicated... + + $cells = $table->get_cellmap()->get_spanned_cells($frame); + + if (is_null($cells)) { + return; + } + + // Render the background to the padding box, as the cells are + // rendered individually one after another, and we don't want the + // background to overlap an adjacent border + $padding_box = $frame->get_padding_box(); + + $this->_render_background($frame, $padding_box); + $this->_render_collapsed_border($frame, $table); + + // FIXME: Outline should be drawn over other cells + $this->_render_outline($frame, $border_box); } - // The collapsed case is slightly complicated... - // @todo Add support for outlines here + $id = $frame->get_node()->getAttribute("id"); + if (strlen($id) > 0) { + $this->_canvas->add_named_dest($id); + } + // $this->debugBlockLayout($frame, "red", false); + } + + /** + * @param Frame $frame + * @param Table $table + */ + protected function _render_collapsed_border(Frame $frame, Table $table): void + { $cellmap = $table->get_cellmap(); $cells = $cellmap->get_spanned_cells($frame); - - if (is_null($cells)) { - return; - } - $num_rows = $cellmap->get_num_rows(); $num_cols = $cellmap->get_num_cols(); + [$table_x, $table_y] = $table->get_position(); + // Determine the top row spanned by this cell $i = $cells["rows"][0]; $top_row = $cellmap->get_row($i); @@ -79,46 +96,45 @@ class TableCell extends Block // Draw the horizontal borders foreach ($cells["columns"] as $j) { $bp = $cellmap->get_border_properties($i, $j); - - $y = $top_row["y"] - $bp["top"]["width"] / 2; - $col = $cellmap->get_column($j); - $x = $col["x"] - $bp["left"]["width"] / 2; + + $x = $table_x + $col["x"] - $bp["left"]["width"] / 2; + $y = $table_y + $top_row["y"] - $bp["top"]["width"] / 2; $w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"]) / 2; - if ($bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0) { - $widths = array( + if ($bp["top"]["width"] > 0) { + $widths = [ (float)$bp["top"]["width"], (float)$bp["right"]["width"], (float)$bp["bottom"]["width"], (float)$bp["left"]["width"] - ); + ]; + $method = "_border_" . $bp["top"]["style"]; $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top", "square"); } if ($draw_bottom) { $bp = $cellmap->get_border_properties($num_rows - 1, $j); - if ($bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0) { + if ($bp["bottom"]["width"] <= 0) { continue; } - - $y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2; - - $widths = array( + + $widths = [ (float)$bp["top"]["width"], (float)$bp["right"]["width"], (float)$bp["bottom"]["width"], (float)$bp["left"]["width"] - ); + ]; + + $y = $table_y + $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2; + $method = "_border_" . $bp["bottom"]["style"]; $this->$method($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square"); - } } $j = $cells["columns"][0]; - $left_col = $cellmap->get_column($j); if (in_array($num_cols - 1, $cells["columns"])) { @@ -131,21 +147,19 @@ class TableCell extends Block // Draw the vertical borders foreach ($cells["rows"] as $i) { $bp = $cellmap->get_border_properties($i, $j); - - $x = $left_col["x"] - $bp["left"]["width"] / 2; - $row = $cellmap->get_row($i); - $y = $row["y"] - $bp["top"]["width"] / 2; + $x = $table_x + $left_col["x"] - $bp["left"]["width"] / 2; + $y = $table_y + $row["y"] - $bp["top"]["width"] / 2; $h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"]) / 2; - if ($bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0) { - $widths = array( + if ($bp["left"]["width"] > 0) { + $widths = [ (float)$bp["top"]["width"], (float)$bp["right"]["width"], (float)$bp["bottom"]["width"], (float)$bp["left"]["width"] - ); + ]; $method = "_border_" . $bp["left"]["style"]; $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left", "square"); @@ -153,27 +167,22 @@ class TableCell extends Block if ($draw_right) { $bp = $cellmap->get_border_properties($i, $num_cols - 1); - if ($bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0) { + if ($bp["right"]["width"] <= 0) { continue; } - $x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2; - - $widths = array( + $widths = [ (float)$bp["top"]["width"], (float)$bp["right"]["width"], (float)$bp["bottom"]["width"], (float)$bp["left"]["width"] - ); + ]; + + $x = $table_x + $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2; $method = "_border_" . $bp["right"]["style"]; $this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square"); } } - - $id = $frame->get_node()->getAttribute("id"); - if (strlen($id) > 0) { - $this->_canvas->add_named_dest($id); - } } } diff --git a/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableRowGroup.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableRowGroup.php new file mode 100644 index 000000000..295ccde3e --- /dev/null +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/TableRowGroup.php @@ -0,0 +1,40 @@ +get_style(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $border_box = $frame->get_border_box(); + + $this->_render_border($frame, $border_box); + $this->_render_outline($frame, $border_box); + + $id = $frame->get_node()->getAttribute("id"); + if (strlen($id) > 0) { + $this->_canvas->add_named_dest($id); + } + + $this->debugBlockLayout($frame, "red"); + } +} diff --git a/library/vendor/dompdf/src/Renderer/Text.php b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Text.php similarity index 80% rename from library/vendor/dompdf/src/Renderer/Text.php rename to library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Text.php index 51e6e6259..e7baa0a01 100644 --- a/library/vendor/dompdf/src/Renderer/Text.php +++ b/library/vendor/dompdf/vendor/dompdf/dompdf/src/Renderer/Text.php @@ -1,10 +1,7 @@ - * @author Helmut Tischer - * @author Fabien Ménager + * @link https://github.com/dompdf/dompdf * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ namespace Dompdf\Renderer; @@ -45,35 +42,29 @@ class Text extends AbstractRenderer */ function render(Frame $frame) { + $style = $frame->get_style(); $text = $frame->get_text(); + if (trim($text) === "") { return; } - $style = $frame->get_style(); + $this->_set_opacity($frame->get_opacity($style->opacity)); + list($x, $y) = $frame->get_position(); $cb = $frame->get_containing_block(); - if (($ml = $style->margin_left) === "auto" || $ml === "none") { - $ml = 0; - } - - if (($pl = $style->padding_left) === "auto" || $pl === "none") { - $pl = 0; - } - - if (($bl = $style->border_left_width) === "auto" || $bl === "none") { - $bl = 0; - } - - $x += (float)$style->length_in_pt(array($ml, $pl, $bl), $cb["w"]); + $ml = $style->margin_left; + $pl = $style->padding_left; + $bl = $style->border_left_width; + $x += (float) $style->length_in_pt([$ml, $pl, $bl], $cb["w"]); $font = $style->font_family; $size = $style->font_size; $frame_font_size = $frame->get_dompdf()->getFontMetrics()->getFontHeight($font, $size); - $word_spacing = $frame->get_text_spacing() + (float)$style->length_in_pt($style->word_spacing); - $char_spacing = (float)$style->length_in_pt($style->letter_spacing); - $width = $style->width; + $word_spacing = $frame->get_text_spacing() + $style->word_spacing; + $letter_spacing = $style->letter_spacing; + $width = (float) $style->width; /*$text = str_replace( array("{PAGE_NUM}"), @@ -83,7 +74,7 @@ class Text extends AbstractRenderer $this->_canvas->text($x, $y, $text, $font, $size, - $style->color, $word_spacing, $char_spacing); + $style->color, $word_spacing, $letter_spacing); $line = $frame->get_containing_line(); @@ -121,7 +112,7 @@ class Text extends AbstractRenderer // Draw all applicable text-decorations. Start with the root and work our way down. $p = $frame; - $stack = array(); + $stack = []; while ($p = $p->get_parent()) { $stack[] = $p; } @@ -160,8 +151,8 @@ class Text extends AbstractRenderer } if ($this->_dompdf->getOptions()->getDebugLayout() && $this->_dompdf->getOptions()->getDebugLayoutLines()) { - $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size); - $this->_debug_layout(array($x, $y, $text_width + ($line->wc - 1) * $word_spacing, $frame_font_size), "orange", array(0.5, 0.5)); + $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $letter_spacing); + $this->_debug_layout([$x, $y, $text_width, $frame_font_size], "orange", [0.5, 0.5]); } } } diff --git a/library/vendor/dompdf/vendor/masterminds/html5/CREDITS b/library/vendor/dompdf/vendor/masterminds/html5/CREDITS new file mode 100644 index 000000000..c2dbc4b64 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/CREDITS @@ -0,0 +1,11 @@ +Matt Butcher [technosophos] (lead) +Matt Farina [mattfarina] (lead) +Asmir Mustafic [goetas] (contributor) +Edward Z. Yang [ezyang] (contributor) +Geoffrey Sneddon [gsnedders] (contributor) +Kukhar Vasily [ngreduce] (contributor) +Rune Christensen [MrElectronic] (contributor) +Mišo Belica [miso-belica] (contributor) +Asmir Mustafic [goetas] (contributor) +KITAITI Makoto [KitaitiMakoto] (contributor) +Jacob Floyd [cognifloyd] (contributor) diff --git a/library/vendor/dompdf/vendor/masterminds/html5/LICENSE.txt b/library/vendor/dompdf/vendor/masterminds/html5/LICENSE.txt new file mode 100644 index 000000000..3c275b54a --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/LICENSE.txt @@ -0,0 +1,66 @@ +## HTML5-PHP License + +Copyright (c) 2013 The Authors of HTML5-PHP + +Matt Butcher - mattbutcher@google.com +Matt Farina - matt@mattfarina.com +Asmir Mustafic - goetas@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## HTML5Lib License + +Portions of this are based on html5lib's PHP version, which was a +sub-project of html5lib. The following is the list of contributors from +html5lib: + +html5lib: + +Copyright (c) 2006-2009 The Authors + +Contributors: +James Graham - jg307@cam.ac.uk +Anne van Kesteren - annevankesteren@gmail.com +Lachlan Hunt - lachlan.hunt@lachy.id.au +Matt McDonald - kanashii@kanashii.ca +Sam Ruby - rubys@intertwingly.net +Ian Hickson (Google) - ian@hixie.ch +Thomas Broyer - t.broyer@ltgt.net +Jacques Distler - distler@golem.ph.utexas.edu +Henri Sivonen - hsivonen@iki.fi +Adam Barth - abarth@webkit.org +Eric Seidel - eric@webkit.org +The Mozilla Foundation (contributions from Henri Sivonen since 2008) +David Flanagan (Mozilla) - dflanagan@mozilla.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/library/vendor/dompdf/vendor/masterminds/html5/README.md b/library/vendor/dompdf/vendor/masterminds/html5/README.md new file mode 100644 index 000000000..b1ca1e371 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/README.md @@ -0,0 +1,270 @@ +> # UKRAINE NEEDS YOUR HELP NOW! +> +> On 24 February 2022, Russian [President Vladimir Putin ordered an invasion of Ukraine by Russian Armed Forces](https://www.bbc.com/news/world-europe-60504334). +> +> Your support is urgently needed. +> +> - Donate to the volunteers. Here is the volunteer fund helping the Ukrainian army to provide all the necessary equipment: +> https://bank.gov.ua/en/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi or https://savelife.in.ua/en/donate/ +> - Triple-check social media sources. Russian disinformation is attempting to coverup and distort the reality in Ukraine. +> - Help Ukrainian refugees who are fleeing Russian attacks and shellings: https://www.globalcitizen.org/en/content/ways-to-help-ukraine-conflict/ +> - Put pressure on your political representatives to provide help to Ukraine. +> - Believe in the Ukrainian people, they will not surrender, they don't have another Ukraine. +> +> THANK YOU! +---- + +# HTML5-PHP + +HTML5 is a standards-compliant HTML5 parser and writer written entirely in PHP. +It is stable and used in many production websites, and has +well over [five million downloads](https://packagist.org/packages/masterminds/html5). + +HTML5 provides the following features. + +- An HTML5 serializer +- Support for PHP namespaces +- Composer support +- Event-based (SAX-like) parser +- A DOM tree builder +- Interoperability with [QueryPath](https://github.com/technosophos/querypath) +- Runs on **PHP** 5.3.0 or newer + +[![Build Status](https://travis-ci.org/Masterminds/html5-php.png?branch=master)](https://travis-ci.org/Masterminds/html5-php) +[![Latest Stable Version](https://poser.pugx.org/masterminds/html5/v/stable.png)](https://packagist.org/packages/masterminds/html5) +[![Code Coverage](https://scrutinizer-ci.com/g/Masterminds/html5-php/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Masterminds/html5-php/?branch=master) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Masterminds/html5-php/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Masterminds/html5-php/?branch=master) +[![Stability: Sustained](https://masterminds.github.io/stability/sustained.svg)](https://masterminds.github.io/stability/sustained.html) + +## Installation + +Install HTML5-PHP using [composer](http://getcomposer.org/). + +By adding the `masterminds/html5` dependency to your `composer.json` file: + +```json +{ + "require" : { + "masterminds/html5": "^2.0" + }, +} +``` + +By invoking require command via composer executable: + +```bash +composer require masterminds/html5 +``` + +## Basic Usage + +HTML5-PHP has a high-level API and a low-level API. + +Here is how you use the high-level `HTML5` library API: + +```php + + + TEST + + +

Hello World

+

This is a test of the HTML5 parser.

+ + +HERE; + +// Parse the document. $dom is a DOMDocument. +$html5 = new HTML5(); +$dom = $html5->loadHTML($html); + +// Render it as HTML5: +print $html5->saveHTML($dom); + +// Or save it to a file: +$html5->save($dom, 'out.html'); +``` + +The `$dom` created by the parser is a full `DOMDocument` object. And the +`save()` and `saveHTML()` methods will take any DOMDocument. + +### Options + +It is possible to pass in an array of configuration options when loading +an HTML5 document. + +```php +// An associative array of options +$options = array( + 'option_name' => 'option_value', +); + +// Provide the options to the constructor +$html5 = new HTML5($options); + +$dom = $html5->loadHTML($html); +``` + +The following options are supported: + +* `encode_entities` (boolean): Indicates that the serializer should aggressively + encode characters as entities. Without this, it only encodes the bare + minimum. +* `disable_html_ns` (boolean): Prevents the parser from automatically + assigning the HTML5 namespace to the DOM document. This is for + non-namespace aware DOM tools. +* `target_document` (\DOMDocument): A DOM document that will be used as the + destination for the parsed nodes. +* `implicit_namespaces` (array): An assoc array of namespaces that should be + used by the parser. Name is tag prefix, value is NS URI. + +## The Low-Level API + +This library provides the following low-level APIs that you can use to +create more customized HTML5 tools: + +- A SAX-like event-based parser that you can hook into for special kinds +of parsing. +- A flexible error-reporting mechanism that can be tuned to document +syntax checking. +- A DOM implementation that uses PHP's built-in DOM library. + +The unit tests exercise each piece of the API, and every public function +is well-documented. + +### Parser Design + +The parser is designed as follows: + +- The `Scanner` handles scanning on behalf of the parser. +- The `Tokenizer` requests data off of the scanner, parses it, clasifies +it, and sends it to an `EventHandler`. It is a *recursive descent parser.* +- The `EventHandler` receives notifications and data for each specific +semantic event that occurs during tokenization. +- The `DOMBuilder` is an `EventHandler` that listens for tokenizing +events and builds a document tree (`DOMDocument`) based on the events. + +### Serializer Design + +The serializer takes a data structure (the `DOMDocument`) and transforms +it into a character representation -- an HTML5 document. + +The serializer is broken into three parts: + +- The `OutputRules` contain the rules to turn DOM elements into strings. The +rules are an implementation of the interface `RulesInterface` allowing for +different rule sets to be used. +- The `Traverser`, which is a special-purpose tree walker. It visits +each node node in the tree and uses the `OutputRules` to transform the node +into a string. +- `HTML5` manages the `Traverser` and stores the resultant data +in the correct place. + +The serializer (`save()`, `saveHTML()`) follows the +[section 8.9 of the HTML 5.0 spec](http://www.w3.org/TR/2012/CR-html5-20121217/syntax.html#serializing-html-fragments). +So tags are serialized according to these rules: + +- A tag with children: <foo>CHILDREN</foo> +- A tag that cannot have content: <foo> (no closing tag) +- A tag that could have content, but doesn't: <foo></foo> + +## Known Issues (Or, Things We Designed Against the Spec) + +Please check the issue queue for a full list, but the following are +issues known issues that are not presently on the roadmap: + +- Namespaces: HTML5 only [supports a selected list of namespaces](http://www.w3.org/TR/html5/infrastructure.html#namespaces) + and they do not operate in the same way as XML namespaces. A `:` has no special + meaning. + By default the parser does not support XML style namespaces via `:`; + to enable the XML namespaces see the [XML Namespaces section](#xml-namespaces) +- Scripts: This parser does not contain a JavaScript or a CSS + interpreter. While one may be supplied, not all features will be + supported. +- Rentrance: The current parser is not re-entrant. (Thus you can't pause + the parser to modify the HTML string mid-parse.) +- Validation: The current tree builder is **not** a validating parser. + While it will correct some HTML, it does not check that the HTML + conforms to the standard. (Should you wish, you can build a validating + parser by extending DOMTree or building your own EventHandler + implementation.) + * There is limited support for insertion modes. + * Some autocorrection is done automatically. + * Per the spec, many legacy tags are admitted and correctly handled, + even though they are technically not part of HTML5. +- Attribute names and values: Due to the implementation details of the + PHP implementation of DOM, attribute names that do not follow the + XML 1.0 standard are not inserted into the DOM. (Effectively, they + are ignored.) If you've got a clever fix for this, jump in! +- Processor Instructions: The HTML5 spec does not allow processor + instructions. We do. Since this is a server-side library, we think + this is useful. And that means, dear reader, that in some cases you + can parse the HTML from a mixed PHP/HTML document. This, however, + is an incidental feature, not a core feature. +- HTML manifests: Unsupported. +- PLAINTEXT: Unsupported. +- Adoption Agency Algorithm: Not yet implemented. (8.2.5.4.7) + +## XML Namespaces + +To use XML style namespaces you have to configure well the main `HTML5` instance. + +```php +use Masterminds\HTML5; +$html = new HTML5(array( + "xmlNamespaces" => true +)); + +$dom = $html->loadHTML(''); + +$dom->documentElement->namespaceURI; // http://www.example.com + +``` + +You can also add some default prefixes that will not require the namespace declaration, +but its elements will be namespaced. + +```php +use Masterminds\HTML5; +$html = new HTML5(array( + "implicitNamespaces"=>array( + "t"=>"http://www.example.com" + ) +)); + +$dom = $html->loadHTML(''); + +$dom->documentElement->namespaceURI; // http://www.example.com + +``` + +## Thanks to... + +The dedicated (and patient) contributors of patches small and large, +who have already made this library better.See the CREDITS file for +a list of contributors. + +We owe a huge debt of gratitude to the original authors of html5lib. + +While not much of the original parser remains, we learned a lot from +reading the html5lib library. And some pieces remain here. In +particular, much of the UTF-8 and Unicode handling is derived from the +html5lib project. + +## License + +This software is released under the MIT license. The original html5lib +library was also released under the MIT license. + +See LICENSE.txt + +Certain files contain copyright assertions by specific individuals +involved with html5lib. Those have been retained where appropriate. diff --git a/library/vendor/dompdf/vendor/masterminds/html5/RELEASE.md b/library/vendor/dompdf/vendor/masterminds/html5/RELEASE.md new file mode 100644 index 000000000..33007ed69 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/RELEASE.md @@ -0,0 +1,157 @@ +# Release Notes + +2.7.6 (2021-08-18) + +- #218: Address comment handling issues + +2.7.5 (2021-07-01) + +- #204: Travis: Enable tests on PHP 8.0 +- #207: Fix PHP 8.1 deprecations + +2.7.4 (2020-10-01) + +- #191: Fix travisci build +- #195: Add .gitattributes file with export-ignore rules +- #194: Fix query parameter parsed as character entity + +2.7.3 (2020-07-05) + +- #190: mitigate cyclic reference between output rules and the traverser objects + +2.7.2 (2020-07-01) + +- #187: Fixed memory leak in HTML5::saveHTML() +- #186: Add special case for end tag
+ +2.7.1 (2020-06-14) + +- #171: add PHP 7.4 job +- #178: Prevent infinite loop on un-terminated entity declaration at EOF + +2.7.0 (2019-07-25) + +- #164: Drop HHVM support +- #168: Set default encoding in the DOMDocument object + +2.6.0 (2019-03-10) + +- #163: Allow to pass a charset to the Scanner + +2.5.0 (2018-12-27) + +- #162, #161, #155, #154, #153, #151: big performance improvements +- #156: fixed typos +- #160: adopt and enforce code style +- #159: remove deprecated php unit base test case +- #150: backport changes from old master branch + +2.4.0 (2018-11-17) + +- #148: Improve performance by moving sequence matching +- #147: Improve the Tokenizer performance +- #146: Improve performance by relying on a native string instead of InputStream +- #144: Add DOM extension in composer.json +- #145: Add more extensions on composer.json, improve phpdocs and remove dead code +- #143: Remove experimental comment + +2.3.1 (2018-10-18) + +- #121: Audio is not a block tag (fixed by #141) +- #136: Handle illegal self-closing according to spec (fixed by #137) +- #141: Minor fixes in the README + +2.3.0 (2017-09-04) + +- #129: image within inline svg breaks system (fixed by #133) +- #131: ² does not work (fixed by #132) +- #134: Improve tokenizer performance by 20% (alternative version of #130 thanks to @MichaelHeerklotz) +- #135: Raw & in attributes + +2.2.2 (2016-09-22) + +- #116: In XML mode, tags are case sensitive +- #115: Fix PHP Notice in OutputRules +- #112: fix parsing of options of an optgroup +- #111: Adding test for the address tag + +2.2.1 (2016-05-10) + +- #109: Fixed issue where address tag could be written without closing tag (thanks sylus) + +2.2.0 (2016-04-11) + +- #105: Enable composer cache (for CI/CD) +- #100: Use mb_substitute_character inset of ini_set for environments where ini_set is disable (e.g., shared hosting) +- #98: Allow link, meta, style tags in noscript tags +- #96: Fixed xml:href on svgs that use the "use" breaking +- #94: Counting UTF8 characters performance improvement +- #93: Use newer version of coveralls package +- #90: Remove duplicate test +- #87: Allow multiple root nodes + +2.1.2 (2015-06-07) +- #82: Support for PHP7 +- #84: Improved boolean attribute handling + +2.1.1 (2015-03-23) +- #78: Fixes bug where unmatched entity like string drops everything after &. + +2.1.0 (2015-02-01) +- #74: Added `disable_html_ns` and `target_doc` dom parsing options +- Unified option names +- #73: Fixed alphabet, ß now can be detected +- #75 and #76: Allow whitespace in RCDATA tags +- #77: Fixed parsing blunder for json embeds +- #72: Add options to HTML methods + +2.0.2 (2014-12-17) +- #50: empty document handling +- #63: tags with strange capitalization +- #65: dashes and underscores as allowed characters in tag names +- #68: Fixed issue with non-inline elements inside inline containers + +2.0.1 (2014-09-23) +- #59: Fixed issue parsing some fragments. +- #56: Incorrectly saw 0 as empty string +- Sami as new documentation generator + +2.0.0 (2014-07-28) +- #53: Improved boolean attributes handling +- #52: Facebook HHVM compatibility +- #48: Adopted PSR-2 as coding standard +- #47: Moved everything to Masterminds namespace +- #45: Added custom namespaces +- #44: Added support to XML-style namespaces +- #37: Refactored HTML5 class removing static methods + +1.0.5 (2014-06-10) +- #38: Set the dev-master branch as the 1.0.x branch for composer (goetas) +- #34: Tests use PSR-4 for autoloading. (goetas) +- #40, #41: Fix entity handling in RCDATA sections. (KitaitiMakoto) +- #32: Fixed issue where wharacter references were being incorrectly encoded in style tags. + +1.0.4 (2014-04-29) +- #30/#31 Don't throw an exception for invalid tag names. + +1.0.3 (2014-02-28) +- #23 and #29: Ignore attributes with illegal chars in name for the PHP DOM. + +1.0.2 (2014-02-12) +- #23: Handle missing tag close in attribute list. +- #25: Fixed text escaping in the serializer (HTML% 8.3). +- #27: Fixed tests on Windows: changed "\n" -> PHP_EOL. +- #28: Fixed infinite loop for char "&" in unquoted attribute in parser. +- #26: Updated tag name case handling to deal with uppercase usage. +- #24: Newlines and tabs are allowed inside quoted attributes (HTML5 8.2.4). +- Fixed Travis CI testing. + +1.0.1 (2013-11-07) +- CDATA encoding is improved. (Non-standard; Issue #19) +- Some parser rules were not returning the new current element. (Issue #20) +- Added, to the README, details on code test coverage and to packagist version. +- Fixed processor instructions. +- Improved test coverage and documentation coverage. + +1.0.0 (2013-10-02) +- Initial release. diff --git a/library/vendor/dompdf/vendor/masterminds/html5/UPGRADING.md b/library/vendor/dompdf/vendor/masterminds/html5/UPGRADING.md new file mode 100644 index 000000000..76e3a19bc --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/UPGRADING.md @@ -0,0 +1,21 @@ +From 1.x to 2.x +================= + +- All classes uses `Masterminds` namespace. +- All public static methods has been removed from `HTML5` class and the general API to access the HTML5 functionalities has changed. + + Before: + + $dom = \HTML5::loadHTML('....'); + \HTML5::saveHTML($dom); + + After: + + use Masterminds\HTML5; + + $html5 = new HTML5(); + + $dom = $html5->loadHTML('....'); + echo $html5->saveHTML($dom); + + diff --git a/library/vendor/dompdf/vendor/masterminds/html5/bin/entities.php b/library/vendor/dompdf/vendor/masterminds/html5/bin/entities.php new file mode 100644 index 000000000..56323a341 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/bin/entities.php @@ -0,0 +1,26 @@ + $obj) { + $sname = substr($name, 1, -1); + $table[$sname] = $obj->characters; +} + +echo '=5.3.0" + }, + "require-dev": { + "phpunit/phpunit" : "^4.8.35 || ^5.7.21 || ^6 || ^7" + }, + "autoload": { + "psr-4": {"Masterminds\\": "src"} + }, + "autoload-dev": { + "psr-4": {"Masterminds\\HTML5\\Tests\\": "test/HTML5"} + }, + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5.php new file mode 100644 index 000000000..c857145fb --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5.php @@ -0,0 +1,246 @@ + false, + + // Prevents the parser from automatically assigning the HTML5 namespace to the DOM document. + 'disable_html_ns' => false, + ); + + protected $errors = array(); + + public function __construct(array $defaultOptions = array()) + { + $this->defaultOptions = array_merge($this->defaultOptions, $defaultOptions); + } + + /** + * Get the current default options. + * + * @return array + */ + public function getOptions() + { + return $this->defaultOptions; + } + + /** + * Load and parse an HTML file. + * + * This will apply the HTML5 parser, which is tolerant of many + * varieties of HTML, including XHTML 1, HTML 4, and well-formed HTML + * 3. Note that in these cases, not all of the old data will be + * preserved. For example, XHTML's XML declaration will be removed. + * + * The rules governing parsing are set out in the HTML 5 spec. + * + * @param string|resource $file The path to the file to parse. If this is a resource, it is + * assumed to be an open stream whose pointer is set to the first + * byte of input. + * @param array $options Configuration options when parsing the HTML. + * + * @return \DOMDocument A DOM document. These object type is defined by the libxml + * library, and should have been included with your version of PHP. + */ + public function load($file, array $options = array()) + { + // Handle the case where file is a resource. + if (is_resource($file)) { + return $this->parse(stream_get_contents($file), $options); + } + + return $this->parse(file_get_contents($file), $options); + } + + /** + * Parse a HTML Document from a string. + * + * Take a string of HTML 5 (or earlier) and parse it into a + * DOMDocument. + * + * @param string $string A html5 document as a string. + * @param array $options Configuration options when parsing the HTML. + * + * @return \DOMDocument A DOM document. DOM is part of libxml, which is included with + * almost all distribtions of PHP. + */ + public function loadHTML($string, array $options = array()) + { + return $this->parse($string, $options); + } + + /** + * Convenience function to load an HTML file. + * + * This is here to provide backwards compatibility with the + * PHP DOM implementation. It simply calls load(). + * + * @param string $file The path to the file to parse. If this is a resource, it is + * assumed to be an open stream whose pointer is set to the first + * byte of input. + * @param array $options Configuration options when parsing the HTML. + * + * @return \DOMDocument A DOM document. These object type is defined by the libxml + * library, and should have been included with your version of PHP. + */ + public function loadHTMLFile($file, array $options = array()) + { + return $this->load($file, $options); + } + + /** + * Parse a HTML fragment from a string. + * + * @param string $string the HTML5 fragment as a string + * @param array $options Configuration options when parsing the HTML + * + * @return \DOMDocumentFragment A DOM fragment. The DOM is part of libxml, which is included with + * almost all distributions of PHP. + */ + public function loadHTMLFragment($string, array $options = array()) + { + return $this->parseFragment($string, $options); + } + + /** + * Return all errors encountered into parsing phase. + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + /** + * Return true it some errors were encountered into parsing phase. + * + * @return bool + */ + public function hasErrors() + { + return count($this->errors) > 0; + } + + /** + * Parse an input string. + * + * @param string $input + * @param array $options + * + * @return \DOMDocument + */ + public function parse($input, array $options = array()) + { + $this->errors = array(); + $options = array_merge($this->defaultOptions, $options); + $events = new DOMTreeBuilder(false, $options); + $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8'); + $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML); + + $parser->parse(); + $this->errors = $events->getErrors(); + + return $events->document(); + } + + /** + * Parse an input stream where the stream is a fragment. + * + * Lower-level loading function. This requires an input stream instead + * of a string, file, or resource. + * + * @param string $input The input data to parse in the form of a string. + * @param array $options An array of options. + * + * @return \DOMDocumentFragment + */ + public function parseFragment($input, array $options = array()) + { + $options = array_merge($this->defaultOptions, $options); + $events = new DOMTreeBuilder(true, $options); + $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8'); + $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML); + + $parser->parse(); + $this->errors = $events->getErrors(); + + return $events->fragment(); + } + + /** + * Save a DOM into a given file as HTML5. + * + * @param mixed $dom The DOM to be serialized. + * @param string|resource $file The filename to be written or resource to write to. + * @param array $options Configuration options when serializing the DOM. These include: + * - encode_entities: Text written to the output is escaped by default and not all + * entities are encoded. If this is set to true all entities will be encoded. + * Defaults to false. + */ + public function save($dom, $file, $options = array()) + { + $close = true; + if (is_resource($file)) { + $stream = $file; + $close = false; + } else { + $stream = fopen($file, 'wb'); + } + $options = array_merge($this->defaultOptions, $options); + $rules = new OutputRules($stream, $options); + $trav = new Traverser($dom, $stream, $rules, $options); + + $trav->walk(); + /* + * release the traverser to avoid cyclic references and allow PHP to free memory without waiting for gc_collect_cycles + */ + $rules->unsetTraverser(); + if ($close) { + fclose($stream); + } + } + + /** + * Convert a DOM into an HTML5 string. + * + * @param mixed $dom The DOM to be serialized. + * @param array $options Configuration options when serializing the DOM. These include: + * - encode_entities: Text written to the output is escaped by default and not all + * entities are encoded. If this is set to true all entities will be encoded. + * Defaults to false. + * + * @return string A HTML5 documented generated from the DOM. + */ + public function saveHTML($dom, $options = array()) + { + $stream = fopen('php://temp', 'wb'); + $this->save($dom, $stream, array_merge($this->defaultOptions, $options)); + + $html = stream_get_contents($stream, -1, 0); + + fclose($stream); + + return $html; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Elements.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Elements.php new file mode 100644 index 000000000..8fe798789 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Elements.php @@ -0,0 +1,619 @@ + 1, + 'abbr' => 1, + 'address' => 65, // NORMAL | BLOCK_TAG + 'area' => 9, // NORMAL | VOID_TAG + 'article' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'aside' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'audio' => 1, // NORMAL + 'b' => 1, + 'base' => 9, // NORMAL | VOID_TAG + 'bdi' => 1, + 'bdo' => 1, + 'blockquote' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'body' => 1, + 'br' => 9, // NORMAL | VOID_TAG + 'button' => 1, + 'canvas' => 65, // NORMAL | BLOCK_TAG + 'caption' => 1, + 'cite' => 1, + 'code' => 1, + 'col' => 9, // NORMAL | VOID_TAG + 'colgroup' => 1, + 'command' => 9, // NORMAL | VOID_TAG + // "data" => 1, // This is highly experimental and only part of the whatwg spec (not w3c). See https://developer.mozilla.org/en-US/docs/HTML/Element/data + 'datalist' => 1, + 'dd' => 65, // NORMAL | BLOCK_TAG + 'del' => 1, + 'details' => 17, // NORMAL | AUTOCLOSE_P, + 'dfn' => 1, + 'dialog' => 17, // NORMAL | AUTOCLOSE_P, + 'div' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'dl' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'dt' => 1, + 'em' => 1, + 'embed' => 9, // NORMAL | VOID_TAG + 'fieldset' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'figcaption' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'figure' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'footer' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'form' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'h1' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'h2' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'h3' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'h4' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'h5' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'h6' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'head' => 1, + 'header' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'hgroup' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'hr' => 73, // NORMAL | VOID_TAG + 'html' => 1, + 'i' => 1, + 'iframe' => 3, // NORMAL | TEXT_RAW + 'img' => 9, // NORMAL | VOID_TAG + 'input' => 9, // NORMAL | VOID_TAG + 'kbd' => 1, + 'ins' => 1, + 'keygen' => 9, // NORMAL | VOID_TAG + 'label' => 1, + 'legend' => 1, + 'li' => 1, + 'link' => 9, // NORMAL | VOID_TAG + 'map' => 1, + 'mark' => 1, + 'menu' => 17, // NORMAL | AUTOCLOSE_P, + 'meta' => 9, // NORMAL | VOID_TAG + 'meter' => 1, + 'nav' => 17, // NORMAL | AUTOCLOSE_P, + 'noscript' => 65, // NORMAL | BLOCK_TAG + 'object' => 1, + 'ol' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'optgroup' => 1, + 'option' => 1, + 'output' => 65, // NORMAL | BLOCK_TAG + 'p' => 209, // NORMAL | AUTOCLOSE_P | BLOCK_TAG | BLOCK_ONLY_INLINE + 'param' => 9, // NORMAL | VOID_TAG + 'pre' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'progress' => 1, + 'q' => 1, + 'rp' => 1, + 'rt' => 1, + 'ruby' => 1, + 's' => 1, + 'samp' => 1, + 'script' => 3, // NORMAL | TEXT_RAW + 'section' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'select' => 1, + 'small' => 1, + 'source' => 9, // NORMAL | VOID_TAG + 'span' => 1, + 'strong' => 1, + 'style' => 3, // NORMAL | TEXT_RAW + 'sub' => 1, + 'summary' => 17, // NORMAL | AUTOCLOSE_P, + 'sup' => 1, + 'table' => 65, // NORMAL | BLOCK_TAG + 'tbody' => 1, + 'td' => 1, + 'textarea' => 5, // NORMAL | TEXT_RCDATA + 'tfoot' => 65, // NORMAL | BLOCK_TAG + 'th' => 1, + 'thead' => 1, + 'time' => 1, + 'title' => 5, // NORMAL | TEXT_RCDATA + 'tr' => 1, + 'track' => 9, // NORMAL | VOID_TAG + 'u' => 1, + 'ul' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG + 'var' => 1, + 'video' => 65, // NORMAL | BLOCK_TAG + 'wbr' => 9, // NORMAL | VOID_TAG + + // Legacy? + 'basefont' => 8, // VOID_TAG + 'bgsound' => 8, // VOID_TAG + 'noframes' => 2, // RAW_TEXT + 'frame' => 9, // NORMAL | VOID_TAG + 'frameset' => 1, + 'center' => 16, + 'dir' => 16, + 'listing' => 16, // AUTOCLOSE_P + 'plaintext' => 48, // AUTOCLOSE_P | TEXT_PLAINTEXT + 'applet' => 0, + 'marquee' => 0, + 'isindex' => 8, // VOID_TAG + 'xmp' => 20, // AUTOCLOSE_P | VOID_TAG | RAW_TEXT + 'noembed' => 2, // RAW_TEXT + ); + + /** + * The MathML elements. + * See http://www.w3.org/wiki/MathML/Elements. + * + * In our case we are only concerned with presentation MathML and not content + * MathML. There is a nice list of this subset at https://developer.mozilla.org/en-US/docs/MathML/Element. + * + * @var array + */ + public static $mathml = array( + 'maction' => 1, + 'maligngroup' => 1, + 'malignmark' => 1, + 'math' => 1, + 'menclose' => 1, + 'merror' => 1, + 'mfenced' => 1, + 'mfrac' => 1, + 'mglyph' => 1, + 'mi' => 1, + 'mlabeledtr' => 1, + 'mlongdiv' => 1, + 'mmultiscripts' => 1, + 'mn' => 1, + 'mo' => 1, + 'mover' => 1, + 'mpadded' => 1, + 'mphantom' => 1, + 'mroot' => 1, + 'mrow' => 1, + 'ms' => 1, + 'mscarries' => 1, + 'mscarry' => 1, + 'msgroup' => 1, + 'msline' => 1, + 'mspace' => 1, + 'msqrt' => 1, + 'msrow' => 1, + 'mstack' => 1, + 'mstyle' => 1, + 'msub' => 1, + 'msup' => 1, + 'msubsup' => 1, + 'mtable' => 1, + 'mtd' => 1, + 'mtext' => 1, + 'mtr' => 1, + 'munder' => 1, + 'munderover' => 1, + ); + + /** + * The svg elements. + * + * The Mozilla documentation has a good list at https://developer.mozilla.org/en-US/docs/SVG/Element. + * The w3c list appears to be lacking in some areas like filter effect elements. + * That list can be found at http://www.w3.org/wiki/SVG/Elements. + * + * Note, FireFox appears to do a better job rendering filter effects than chrome. + * While they are in the spec I'm not sure how widely implemented they are. + * + * @var array + */ + public static $svg = array( + 'a' => 1, + 'altGlyph' => 1, + 'altGlyphDef' => 1, + 'altGlyphItem' => 1, + 'animate' => 1, + 'animateColor' => 1, + 'animateMotion' => 1, + 'animateTransform' => 1, + 'circle' => 1, + 'clipPath' => 1, + 'color-profile' => 1, + 'cursor' => 1, + 'defs' => 1, + 'desc' => 1, + 'ellipse' => 1, + 'feBlend' => 1, + 'feColorMatrix' => 1, + 'feComponentTransfer' => 1, + 'feComposite' => 1, + 'feConvolveMatrix' => 1, + 'feDiffuseLighting' => 1, + 'feDisplacementMap' => 1, + 'feDistantLight' => 1, + 'feFlood' => 1, + 'feFuncA' => 1, + 'feFuncB' => 1, + 'feFuncG' => 1, + 'feFuncR' => 1, + 'feGaussianBlur' => 1, + 'feImage' => 1, + 'feMerge' => 1, + 'feMergeNode' => 1, + 'feMorphology' => 1, + 'feOffset' => 1, + 'fePointLight' => 1, + 'feSpecularLighting' => 1, + 'feSpotLight' => 1, + 'feTile' => 1, + 'feTurbulence' => 1, + 'filter' => 1, + 'font' => 1, + 'font-face' => 1, + 'font-face-format' => 1, + 'font-face-name' => 1, + 'font-face-src' => 1, + 'font-face-uri' => 1, + 'foreignObject' => 1, + 'g' => 1, + 'glyph' => 1, + 'glyphRef' => 1, + 'hkern' => 1, + 'image' => 1, + 'line' => 1, + 'linearGradient' => 1, + 'marker' => 1, + 'mask' => 1, + 'metadata' => 1, + 'missing-glyph' => 1, + 'mpath' => 1, + 'path' => 1, + 'pattern' => 1, + 'polygon' => 1, + 'polyline' => 1, + 'radialGradient' => 1, + 'rect' => 1, + 'script' => 3, // NORMAL | RAW_TEXT + 'set' => 1, + 'stop' => 1, + 'style' => 3, // NORMAL | RAW_TEXT + 'svg' => 1, + 'switch' => 1, + 'symbol' => 1, + 'text' => 1, + 'textPath' => 1, + 'title' => 1, + 'tref' => 1, + 'tspan' => 1, + 'use' => 1, + 'view' => 1, + 'vkern' => 1, + ); + + /** + * Some attributes in SVG are case sensitive. + * + * This map contains key/value pairs with the key as the lowercase attribute + * name and the value with the correct casing. + */ + public static $svgCaseSensitiveAttributeMap = array( + 'attributename' => 'attributeName', + 'attributetype' => 'attributeType', + 'basefrequency' => 'baseFrequency', + 'baseprofile' => 'baseProfile', + 'calcmode' => 'calcMode', + 'clippathunits' => 'clipPathUnits', + 'contentscripttype' => 'contentScriptType', + 'contentstyletype' => 'contentStyleType', + 'diffuseconstant' => 'diffuseConstant', + 'edgemode' => 'edgeMode', + 'externalresourcesrequired' => 'externalResourcesRequired', + 'filterres' => 'filterRes', + 'filterunits' => 'filterUnits', + 'glyphref' => 'glyphRef', + 'gradienttransform' => 'gradientTransform', + 'gradientunits' => 'gradientUnits', + 'kernelmatrix' => 'kernelMatrix', + 'kernelunitlength' => 'kernelUnitLength', + 'keypoints' => 'keyPoints', + 'keysplines' => 'keySplines', + 'keytimes' => 'keyTimes', + 'lengthadjust' => 'lengthAdjust', + 'limitingconeangle' => 'limitingConeAngle', + 'markerheight' => 'markerHeight', + 'markerunits' => 'markerUnits', + 'markerwidth' => 'markerWidth', + 'maskcontentunits' => 'maskContentUnits', + 'maskunits' => 'maskUnits', + 'numoctaves' => 'numOctaves', + 'pathlength' => 'pathLength', + 'patterncontentunits' => 'patternContentUnits', + 'patterntransform' => 'patternTransform', + 'patternunits' => 'patternUnits', + 'pointsatx' => 'pointsAtX', + 'pointsaty' => 'pointsAtY', + 'pointsatz' => 'pointsAtZ', + 'preservealpha' => 'preserveAlpha', + 'preserveaspectratio' => 'preserveAspectRatio', + 'primitiveunits' => 'primitiveUnits', + 'refx' => 'refX', + 'refy' => 'refY', + 'repeatcount' => 'repeatCount', + 'repeatdur' => 'repeatDur', + 'requiredextensions' => 'requiredExtensions', + 'requiredfeatures' => 'requiredFeatures', + 'specularconstant' => 'specularConstant', + 'specularexponent' => 'specularExponent', + 'spreadmethod' => 'spreadMethod', + 'startoffset' => 'startOffset', + 'stddeviation' => 'stdDeviation', + 'stitchtiles' => 'stitchTiles', + 'surfacescale' => 'surfaceScale', + 'systemlanguage' => 'systemLanguage', + 'tablevalues' => 'tableValues', + 'targetx' => 'targetX', + 'targety' => 'targetY', + 'textlength' => 'textLength', + 'viewbox' => 'viewBox', + 'viewtarget' => 'viewTarget', + 'xchannelselector' => 'xChannelSelector', + 'ychannelselector' => 'yChannelSelector', + 'zoomandpan' => 'zoomAndPan', + ); + + /** + * Some SVG elements are case sensitive. + * This map contains these. + * + * The map contains key/value store of the name is lowercase as the keys and + * the correct casing as the value. + */ + public static $svgCaseSensitiveElementMap = array( + 'altglyph' => 'altGlyph', + 'altglyphdef' => 'altGlyphDef', + 'altglyphitem' => 'altGlyphItem', + 'animatecolor' => 'animateColor', + 'animatemotion' => 'animateMotion', + 'animatetransform' => 'animateTransform', + 'clippath' => 'clipPath', + 'feblend' => 'feBlend', + 'fecolormatrix' => 'feColorMatrix', + 'fecomponenttransfer' => 'feComponentTransfer', + 'fecomposite' => 'feComposite', + 'feconvolvematrix' => 'feConvolveMatrix', + 'fediffuselighting' => 'feDiffuseLighting', + 'fedisplacementmap' => 'feDisplacementMap', + 'fedistantlight' => 'feDistantLight', + 'feflood' => 'feFlood', + 'fefunca' => 'feFuncA', + 'fefuncb' => 'feFuncB', + 'fefuncg' => 'feFuncG', + 'fefuncr' => 'feFuncR', + 'fegaussianblur' => 'feGaussianBlur', + 'feimage' => 'feImage', + 'femerge' => 'feMerge', + 'femergenode' => 'feMergeNode', + 'femorphology' => 'feMorphology', + 'feoffset' => 'feOffset', + 'fepointlight' => 'fePointLight', + 'fespecularlighting' => 'feSpecularLighting', + 'fespotlight' => 'feSpotLight', + 'fetile' => 'feTile', + 'feturbulence' => 'feTurbulence', + 'foreignobject' => 'foreignObject', + 'glyphref' => 'glyphRef', + 'lineargradient' => 'linearGradient', + 'radialgradient' => 'radialGradient', + 'textpath' => 'textPath', + ); + + /** + * Check whether the given element meets the given criterion. + * + * Example: + * + * Elements::isA('script', Elements::TEXT_RAW); // Returns true. + * + * Elements::isA('script', Elements::TEXT_RCDATA); // Returns false. + * + * @param string $name The element name. + * @param int $mask One of the constants on this class. + * + * @return bool true if the element matches the mask, false otherwise. + */ + public static function isA($name, $mask) + { + return (static::element($name) & $mask) === $mask; + } + + /** + * Test if an element is a valid html5 element. + * + * @param string $name The name of the element. + * + * @return bool true if a html5 element and false otherwise. + */ + public static function isHtml5Element($name) + { + // html5 element names are case insensitive. Forcing lowercase for the check. + // Do we need this check or will all data passed here already be lowercase? + return isset(static::$html5[strtolower($name)]); + } + + /** + * Test if an element name is a valid MathML presentation element. + * + * @param string $name The name of the element. + * + * @return bool true if a MathML name and false otherwise. + */ + public static function isMathMLElement($name) + { + // MathML is case-sensitive unlike html5 elements. + return isset(static::$mathml[$name]); + } + + /** + * Test if an element is a valid SVG element. + * + * @param string $name The name of the element. + * + * @return bool true if a SVG element and false otherise. + */ + public static function isSvgElement($name) + { + // SVG is case-sensitive unlike html5 elements. + return isset(static::$svg[$name]); + } + + /** + * Is an element name valid in an html5 document. + * This includes html5 elements along with other allowed embedded content + * such as svg and mathml. + * + * @param string $name The name of the element. + * + * @return bool true if valid and false otherwise. + */ + public static function isElement($name) + { + return static::isHtml5Element($name) || static::isMathMLElement($name) || static::isSvgElement($name); + } + + /** + * Get the element mask for the given element name. + * + * @param string $name The name of the element. + * + * @return int the element mask. + */ + public static function element($name) + { + if (isset(static::$html5[$name])) { + return static::$html5[$name]; + } + if (isset(static::$svg[$name])) { + return static::$svg[$name]; + } + if (isset(static::$mathml[$name])) { + return static::$mathml[$name]; + } + + return 0; + } + + /** + * Normalize a SVG element name to its proper case and form. + * + * @param string $name The name of the element. + * + * @return string the normalized form of the element name. + */ + public static function normalizeSvgElement($name) + { + $name = strtolower($name); + if (isset(static::$svgCaseSensitiveElementMap[$name])) { + $name = static::$svgCaseSensitiveElementMap[$name]; + } + + return $name; + } + + /** + * Normalize a SVG attribute name to its proper case and form. + * + * @param string $name The name of the attribute. + * + * @return string The normalized form of the attribute name. + */ + public static function normalizeSvgAttribute($name) + { + $name = strtolower($name); + if (isset(static::$svgCaseSensitiveAttributeMap[$name])) { + $name = static::$svgCaseSensitiveAttributeMap[$name]; + } + + return $name; + } + + /** + * Normalize a MathML attribute name to its proper case and form. + * Note, all MathML element names are lowercase. + * + * @param string $name The name of the attribute. + * + * @return string The normalized form of the attribute name. + */ + public static function normalizeMathMlAttribute($name) + { + $name = strtolower($name); + + // Only one attribute has a mixed case form for MathML. + if ('definitionurl' === $name) { + $name = 'definitionURL'; + } + + return $name; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Entities.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Entities.php new file mode 100644 index 000000000..0e7227dc1 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Entities.php @@ -0,0 +1,2236 @@ + 'Á', + 'Aacut' => 'Á', + 'aacute' => 'á', + 'aacut' => 'á', + 'Abreve' => 'Ă', + 'abreve' => 'ă', + 'ac' => '∾', + 'acd' => '∿', + 'acE' => '∾̳', + 'Acirc' => 'Â', + 'Acir' => 'Â', + 'acirc' => 'â', + 'acir' => 'â', + 'acute' => '´', + 'acut' => '´', + 'Acy' => 'А', + 'acy' => 'а', + 'AElig' => 'Æ', + 'AEli' => 'Æ', + 'aelig' => 'æ', + 'aeli' => 'æ', + 'af' => '⁡', + 'Afr' => '𝔄', + 'afr' => '𝔞', + 'Agrave' => 'À', + 'Agrav' => 'À', + 'agrave' => 'à', + 'agrav' => 'à', + 'alefsym' => 'ℵ', + 'aleph' => 'ℵ', + 'Alpha' => 'Α', + 'alpha' => 'α', + 'Amacr' => 'Ā', + 'amacr' => 'ā', + 'amalg' => '⨿', + 'AMP' => '&', + 'AM' => '&', + 'amp' => '&', + 'am' => '&', + 'And' => '⩓', + 'and' => '∧', + 'andand' => '⩕', + 'andd' => '⩜', + 'andslope' => '⩘', + 'andv' => '⩚', + 'ang' => '∠', + 'ange' => '⦤', + 'angle' => '∠', + 'angmsd' => '∡', + 'angmsdaa' => '⦨', + 'angmsdab' => '⦩', + 'angmsdac' => '⦪', + 'angmsdad' => '⦫', + 'angmsdae' => '⦬', + 'angmsdaf' => '⦭', + 'angmsdag' => '⦮', + 'angmsdah' => '⦯', + 'angrt' => '∟', + 'angrtvb' => '⊾', + 'angrtvbd' => '⦝', + 'angsph' => '∢', + 'angst' => 'Å', + 'angzarr' => '⍼', + 'Aogon' => 'Ą', + 'aogon' => 'ą', + 'Aopf' => '𝔸', + 'aopf' => '𝕒', + 'ap' => '≈', + 'apacir' => '⩯', + 'apE' => '⩰', + 'ape' => '≊', + 'apid' => '≋', + 'apos' => '\'', + 'ApplyFunction' => '⁡', + 'approx' => '≈', + 'approxeq' => '≊', + 'Aring' => 'Å', + 'Arin' => 'Å', + 'aring' => 'å', + 'arin' => 'å', + 'Ascr' => '𝒜', + 'ascr' => '𝒶', + 'Assign' => '≔', + 'ast' => '*', + 'asymp' => '≈', + 'asympeq' => '≍', + 'Atilde' => 'Ã', + 'Atild' => 'Ã', + 'atilde' => 'ã', + 'atild' => 'ã', + 'Auml' => 'Ä', + 'Aum' => 'Ä', + 'auml' => 'ä', + 'aum' => 'ä', + 'awconint' => '∳', + 'awint' => '⨑', + 'backcong' => '≌', + 'backepsilon' => '϶', + 'backprime' => '‵', + 'backsim' => '∽', + 'backsimeq' => '⋍', + 'Backslash' => '∖', + 'Barv' => '⫧', + 'barvee' => '⊽', + 'Barwed' => '⌆', + 'barwed' => '⌅', + 'barwedge' => '⌅', + 'bbrk' => '⎵', + 'bbrktbrk' => '⎶', + 'bcong' => '≌', + 'Bcy' => 'Б', + 'bcy' => 'б', + 'bdquo' => '„', + 'becaus' => '∵', + 'Because' => '∵', + 'because' => '∵', + 'bemptyv' => '⦰', + 'bepsi' => '϶', + 'bernou' => 'ℬ', + 'Bernoullis' => 'ℬ', + 'Beta' => 'Β', + 'beta' => 'β', + 'beth' => 'ℶ', + 'between' => '≬', + 'Bfr' => '𝔅', + 'bfr' => '𝔟', + 'bigcap' => '⋂', + 'bigcirc' => '◯', + 'bigcup' => '⋃', + 'bigodot' => '⨀', + 'bigoplus' => '⨁', + 'bigotimes' => '⨂', + 'bigsqcup' => '⨆', + 'bigstar' => '★', + 'bigtriangledown' => '▽', + 'bigtriangleup' => '△', + 'biguplus' => '⨄', + 'bigvee' => '⋁', + 'bigwedge' => '⋀', + 'bkarow' => '⤍', + 'blacklozenge' => '⧫', + 'blacksquare' => '▪', + 'blacktriangle' => '▴', + 'blacktriangledown' => '▾', + 'blacktriangleleft' => '◂', + 'blacktriangleright' => '▸', + 'blank' => '␣', + 'blk12' => '▒', + 'blk14' => '░', + 'blk34' => '▓', + 'block' => '█', + 'bne' => '=⃥', + 'bnequiv' => '≡⃥', + 'bNot' => '⫭', + 'bnot' => '⌐', + 'Bopf' => '𝔹', + 'bopf' => '𝕓', + 'bot' => '⊥', + 'bottom' => '⊥', + 'bowtie' => '⋈', + 'boxbox' => '⧉', + 'boxDL' => '╗', + 'boxDl' => '╖', + 'boxdL' => '╕', + 'boxdl' => '┐', + 'boxDR' => '╔', + 'boxDr' => '╓', + 'boxdR' => '╒', + 'boxdr' => '┌', + 'boxH' => '═', + 'boxh' => '─', + 'boxHD' => '╦', + 'boxHd' => '╤', + 'boxhD' => '╥', + 'boxhd' => '┬', + 'boxHU' => '╩', + 'boxHu' => '╧', + 'boxhU' => '╨', + 'boxhu' => '┴', + 'boxminus' => '⊟', + 'boxplus' => '⊞', + 'boxtimes' => '⊠', + 'boxUL' => '╝', + 'boxUl' => '╜', + 'boxuL' => '╛', + 'boxul' => '┘', + 'boxUR' => '╚', + 'boxUr' => '╙', + 'boxuR' => '╘', + 'boxur' => '└', + 'boxV' => '║', + 'boxv' => '│', + 'boxVH' => '╬', + 'boxVh' => '╫', + 'boxvH' => '╪', + 'boxvh' => '┼', + 'boxVL' => '╣', + 'boxVl' => '╢', + 'boxvL' => '╡', + 'boxvl' => '┤', + 'boxVR' => '╠', + 'boxVr' => '╟', + 'boxvR' => '╞', + 'boxvr' => '├', + 'bprime' => '‵', + 'Breve' => '˘', + 'breve' => '˘', + 'brvbar' => '¦', + 'brvba' => '¦', + 'Bscr' => 'ℬ', + 'bscr' => '𝒷', + 'bsemi' => '⁏', + 'bsim' => '∽', + 'bsime' => '⋍', + 'bsol' => '\\', + 'bsolb' => '⧅', + 'bsolhsub' => '⟈', + 'bull' => '•', + 'bullet' => '•', + 'bump' => '≎', + 'bumpE' => '⪮', + 'bumpe' => '≏', + 'Bumpeq' => '≎', + 'bumpeq' => '≏', + 'Cacute' => 'Ć', + 'cacute' => 'ć', + 'Cap' => '⋒', + 'cap' => '∩', + 'capand' => '⩄', + 'capbrcup' => '⩉', + 'capcap' => '⩋', + 'capcup' => '⩇', + 'capdot' => '⩀', + 'CapitalDifferentialD' => 'ⅅ', + 'caps' => '∩︀', + 'caret' => '⁁', + 'caron' => 'ˇ', + 'Cayleys' => 'ℭ', + 'ccaps' => '⩍', + 'Ccaron' => 'Č', + 'ccaron' => 'č', + 'Ccedil' => 'Ç', + 'Ccedi' => 'Ç', + 'ccedil' => 'ç', + 'ccedi' => 'ç', + 'Ccirc' => 'Ĉ', + 'ccirc' => 'ĉ', + 'Cconint' => '∰', + 'ccups' => '⩌', + 'ccupssm' => '⩐', + 'Cdot' => 'Ċ', + 'cdot' => 'ċ', + 'cedil' => '¸', + 'cedi' => '¸', + 'Cedilla' => '¸', + 'cemptyv' => '⦲', + 'cent' => '¢', + 'cen' => '¢', + 'CenterDot' => '·', + 'centerdot' => '·', + 'Cfr' => 'ℭ', + 'cfr' => '𝔠', + 'CHcy' => 'Ч', + 'chcy' => 'ч', + 'check' => '✓', + 'checkmark' => '✓', + 'Chi' => 'Χ', + 'chi' => 'χ', + 'cir' => '○', + 'circ' => 'ˆ', + 'circeq' => '≗', + 'circlearrowleft' => '↺', + 'circlearrowright' => '↻', + 'circledast' => '⊛', + 'circledcirc' => '⊚', + 'circleddash' => '⊝', + 'CircleDot' => '⊙', + 'circledR' => '®', + 'circledS' => 'Ⓢ', + 'CircleMinus' => '⊖', + 'CirclePlus' => '⊕', + 'CircleTimes' => '⊗', + 'cirE' => '⧃', + 'cire' => '≗', + 'cirfnint' => '⨐', + 'cirmid' => '⫯', + 'cirscir' => '⧂', + 'ClockwiseContourIntegral' => '∲', + 'CloseCurlyDoubleQuote' => '”', + 'CloseCurlyQuote' => '’', + 'clubs' => '♣', + 'clubsuit' => '♣', + 'Colon' => '∷', + 'colon' => ':', + 'Colone' => '⩴', + 'colone' => '≔', + 'coloneq' => '≔', + 'comma' => ',', + 'commat' => '@', + 'comp' => '∁', + 'compfn' => '∘', + 'complement' => '∁', + 'complexes' => 'ℂ', + 'cong' => '≅', + 'congdot' => '⩭', + 'Congruent' => '≡', + 'Conint' => '∯', + 'conint' => '∮', + 'ContourIntegral' => '∮', + 'Copf' => 'ℂ', + 'copf' => '𝕔', + 'coprod' => '∐', + 'Coproduct' => '∐', + 'COPY' => '©', + 'COP' => '©', + 'copy' => '©', + 'cop' => '©', + 'copysr' => '℗', + 'CounterClockwiseContourIntegral' => '∳', + 'crarr' => '↵', + 'Cross' => '⨯', + 'cross' => '✗', + 'Cscr' => '𝒞', + 'cscr' => '𝒸', + 'csub' => '⫏', + 'csube' => '⫑', + 'csup' => '⫐', + 'csupe' => '⫒', + 'ctdot' => '⋯', + 'cudarrl' => '⤸', + 'cudarrr' => '⤵', + 'cuepr' => '⋞', + 'cuesc' => '⋟', + 'cularr' => '↶', + 'cularrp' => '⤽', + 'Cup' => '⋓', + 'cup' => '∪', + 'cupbrcap' => '⩈', + 'CupCap' => '≍', + 'cupcap' => '⩆', + 'cupcup' => '⩊', + 'cupdot' => '⊍', + 'cupor' => '⩅', + 'cups' => '∪︀', + 'curarr' => '↷', + 'curarrm' => '⤼', + 'curlyeqprec' => '⋞', + 'curlyeqsucc' => '⋟', + 'curlyvee' => '⋎', + 'curlywedge' => '⋏', + 'curren' => '¤', + 'curre' => '¤', + 'curvearrowleft' => '↶', + 'curvearrowright' => '↷', + 'cuvee' => '⋎', + 'cuwed' => '⋏', + 'cwconint' => '∲', + 'cwint' => '∱', + 'cylcty' => '⌭', + 'Dagger' => '‡', + 'dagger' => '†', + 'daleth' => 'ℸ', + 'Darr' => '↡', + 'dArr' => '⇓', + 'darr' => '↓', + 'dash' => '‐', + 'Dashv' => '⫤', + 'dashv' => '⊣', + 'dbkarow' => '⤏', + 'dblac' => '˝', + 'Dcaron' => 'Ď', + 'dcaron' => 'ď', + 'Dcy' => 'Д', + 'dcy' => 'д', + 'DD' => 'ⅅ', + 'dd' => 'ⅆ', + 'ddagger' => '‡', + 'ddarr' => '⇊', + 'DDotrahd' => '⤑', + 'ddotseq' => '⩷', + 'deg' => '°', + 'de' => '°', + 'Del' => '∇', + 'Delta' => 'Δ', + 'delta' => 'δ', + 'demptyv' => '⦱', + 'dfisht' => '⥿', + 'Dfr' => '𝔇', + 'dfr' => '𝔡', + 'dHar' => '⥥', + 'dharl' => '⇃', + 'dharr' => '⇂', + 'DiacriticalAcute' => '´', + 'DiacriticalDot' => '˙', + 'DiacriticalDoubleAcute' => '˝', + 'DiacriticalGrave' => '`', + 'DiacriticalTilde' => '˜', + 'diam' => '⋄', + 'Diamond' => '⋄', + 'diamond' => '⋄', + 'diamondsuit' => '♦', + 'diams' => '♦', + 'die' => '¨', + 'DifferentialD' => 'ⅆ', + 'digamma' => 'ϝ', + 'disin' => '⋲', + 'div' => '÷', + 'divide' => '÷', + 'divid' => '÷', + 'divideontimes' => '⋇', + 'divonx' => '⋇', + 'DJcy' => 'Ђ', + 'djcy' => 'ђ', + 'dlcorn' => '⌞', + 'dlcrop' => '⌍', + 'dollar' => '$', + 'Dopf' => '𝔻', + 'dopf' => '𝕕', + 'Dot' => '¨', + 'dot' => '˙', + 'DotDot' => '⃜', + 'doteq' => '≐', + 'doteqdot' => '≑', + 'DotEqual' => '≐', + 'dotminus' => '∸', + 'dotplus' => '∔', + 'dotsquare' => '⊡', + 'doublebarwedge' => '⌆', + 'DoubleContourIntegral' => '∯', + 'DoubleDot' => '¨', + 'DoubleDownArrow' => '⇓', + 'DoubleLeftArrow' => '⇐', + 'DoubleLeftRightArrow' => '⇔', + 'DoubleLeftTee' => '⫤', + 'DoubleLongLeftArrow' => '⟸', + 'DoubleLongLeftRightArrow' => '⟺', + 'DoubleLongRightArrow' => '⟹', + 'DoubleRightArrow' => '⇒', + 'DoubleRightTee' => '⊨', + 'DoubleUpArrow' => '⇑', + 'DoubleUpDownArrow' => '⇕', + 'DoubleVerticalBar' => '∥', + 'DownArrow' => '↓', + 'Downarrow' => '⇓', + 'downarrow' => '↓', + 'DownArrowBar' => '⤓', + 'DownArrowUpArrow' => '⇵', + 'DownBreve' => '̑', + 'downdownarrows' => '⇊', + 'downharpoonleft' => '⇃', + 'downharpoonright' => '⇂', + 'DownLeftRightVector' => '⥐', + 'DownLeftTeeVector' => '⥞', + 'DownLeftVector' => '↽', + 'DownLeftVectorBar' => '⥖', + 'DownRightTeeVector' => '⥟', + 'DownRightVector' => '⇁', + 'DownRightVectorBar' => '⥗', + 'DownTee' => '⊤', + 'DownTeeArrow' => '↧', + 'drbkarow' => '⤐', + 'drcorn' => '⌟', + 'drcrop' => '⌌', + 'Dscr' => '𝒟', + 'dscr' => '𝒹', + 'DScy' => 'Ѕ', + 'dscy' => 'ѕ', + 'dsol' => '⧶', + 'Dstrok' => 'Đ', + 'dstrok' => 'đ', + 'dtdot' => '⋱', + 'dtri' => '▿', + 'dtrif' => '▾', + 'duarr' => '⇵', + 'duhar' => '⥯', + 'dwangle' => '⦦', + 'DZcy' => 'Џ', + 'dzcy' => 'џ', + 'dzigrarr' => '⟿', + 'Eacute' => 'É', + 'Eacut' => 'É', + 'eacute' => 'é', + 'eacut' => 'é', + 'easter' => '⩮', + 'Ecaron' => 'Ě', + 'ecaron' => 'ě', + 'ecir' => 'ê', + 'Ecirc' => 'Ê', + 'Ecir' => 'Ê', + 'ecirc' => 'ê', + 'ecolon' => '≕', + 'Ecy' => 'Э', + 'ecy' => 'э', + 'eDDot' => '⩷', + 'Edot' => 'Ė', + 'eDot' => '≑', + 'edot' => 'ė', + 'ee' => 'ⅇ', + 'efDot' => '≒', + 'Efr' => '𝔈', + 'efr' => '𝔢', + 'eg' => '⪚', + 'Egrave' => 'È', + 'Egrav' => 'È', + 'egrave' => 'è', + 'egrav' => 'è', + 'egs' => '⪖', + 'egsdot' => '⪘', + 'el' => '⪙', + 'Element' => '∈', + 'elinters' => '⏧', + 'ell' => 'ℓ', + 'els' => '⪕', + 'elsdot' => '⪗', + 'Emacr' => 'Ē', + 'emacr' => 'ē', + 'empty' => '∅', + 'emptyset' => '∅', + 'EmptySmallSquare' => '◻', + 'emptyv' => '∅', + 'EmptyVerySmallSquare' => '▫', + 'emsp' => ' ', + 'emsp13' => ' ', + 'emsp14' => ' ', + 'ENG' => 'Ŋ', + 'eng' => 'ŋ', + 'ensp' => ' ', + 'Eogon' => 'Ę', + 'eogon' => 'ę', + 'Eopf' => '𝔼', + 'eopf' => '𝕖', + 'epar' => '⋕', + 'eparsl' => '⧣', + 'eplus' => '⩱', + 'epsi' => 'ε', + 'Epsilon' => 'Ε', + 'epsilon' => 'ε', + 'epsiv' => 'ϵ', + 'eqcirc' => '≖', + 'eqcolon' => '≕', + 'eqsim' => '≂', + 'eqslantgtr' => '⪖', + 'eqslantless' => '⪕', + 'Equal' => '⩵', + 'equals' => '=', + 'EqualTilde' => '≂', + 'equest' => '≟', + 'Equilibrium' => '⇌', + 'equiv' => '≡', + 'equivDD' => '⩸', + 'eqvparsl' => '⧥', + 'erarr' => '⥱', + 'erDot' => '≓', + 'Escr' => 'ℰ', + 'escr' => 'ℯ', + 'esdot' => '≐', + 'Esim' => '⩳', + 'esim' => '≂', + 'Eta' => 'Η', + 'eta' => 'η', + 'ETH' => 'Ð', + 'ET' => 'Ð', + 'eth' => 'ð', + 'et' => 'ð', + 'Euml' => 'Ë', + 'Eum' => 'Ë', + 'euml' => 'ë', + 'eum' => 'ë', + 'euro' => '€', + 'excl' => '!', + 'exist' => '∃', + 'Exists' => '∃', + 'expectation' => 'ℰ', + 'ExponentialE' => 'ⅇ', + 'exponentiale' => 'ⅇ', + 'fallingdotseq' => '≒', + 'Fcy' => 'Ф', + 'fcy' => 'ф', + 'female' => '♀', + 'ffilig' => 'ffi', + 'fflig' => 'ff', + 'ffllig' => 'ffl', + 'Ffr' => '𝔉', + 'ffr' => '𝔣', + 'filig' => 'fi', + 'FilledSmallSquare' => '◼', + 'FilledVerySmallSquare' => '▪', + 'fjlig' => 'fj', + 'flat' => '♭', + 'fllig' => 'fl', + 'fltns' => '▱', + 'fnof' => 'ƒ', + 'Fopf' => '𝔽', + 'fopf' => '𝕗', + 'ForAll' => '∀', + 'forall' => '∀', + 'fork' => '⋔', + 'forkv' => '⫙', + 'Fouriertrf' => 'ℱ', + 'fpartint' => '⨍', + 'frac12' => '½', + 'frac1' => '¼', + 'frac13' => '⅓', + 'frac14' => '¼', + 'frac15' => '⅕', + 'frac16' => '⅙', + 'frac18' => '⅛', + 'frac23' => '⅔', + 'frac25' => '⅖', + 'frac34' => '¾', + 'frac3' => '¾', + 'frac35' => '⅗', + 'frac38' => '⅜', + 'frac45' => '⅘', + 'frac56' => '⅚', + 'frac58' => '⅝', + 'frac78' => '⅞', + 'frasl' => '⁄', + 'frown' => '⌢', + 'Fscr' => 'ℱ', + 'fscr' => '𝒻', + 'gacute' => 'ǵ', + 'Gamma' => 'Γ', + 'gamma' => 'γ', + 'Gammad' => 'Ϝ', + 'gammad' => 'ϝ', + 'gap' => '⪆', + 'Gbreve' => 'Ğ', + 'gbreve' => 'ğ', + 'Gcedil' => 'Ģ', + 'Gcirc' => 'Ĝ', + 'gcirc' => 'ĝ', + 'Gcy' => 'Г', + 'gcy' => 'г', + 'Gdot' => 'Ġ', + 'gdot' => 'ġ', + 'gE' => '≧', + 'ge' => '≥', + 'gEl' => '⪌', + 'gel' => '⋛', + 'geq' => '≥', + 'geqq' => '≧', + 'geqslant' => '⩾', + 'ges' => '⩾', + 'gescc' => '⪩', + 'gesdot' => '⪀', + 'gesdoto' => '⪂', + 'gesdotol' => '⪄', + 'gesl' => '⋛︀', + 'gesles' => '⪔', + 'Gfr' => '𝔊', + 'gfr' => '𝔤', + 'Gg' => '⋙', + 'gg' => '≫', + 'ggg' => '⋙', + 'gimel' => 'ℷ', + 'GJcy' => 'Ѓ', + 'gjcy' => 'ѓ', + 'gl' => '≷', + 'gla' => '⪥', + 'glE' => '⪒', + 'glj' => '⪤', + 'gnap' => '⪊', + 'gnapprox' => '⪊', + 'gnE' => '≩', + 'gne' => '⪈', + 'gneq' => '⪈', + 'gneqq' => '≩', + 'gnsim' => '⋧', + 'Gopf' => '𝔾', + 'gopf' => '𝕘', + 'grave' => '`', + 'GreaterEqual' => '≥', + 'GreaterEqualLess' => '⋛', + 'GreaterFullEqual' => '≧', + 'GreaterGreater' => '⪢', + 'GreaterLess' => '≷', + 'GreaterSlantEqual' => '⩾', + 'GreaterTilde' => '≳', + 'Gscr' => '𝒢', + 'gscr' => 'ℊ', + 'gsim' => '≳', + 'gsime' => '⪎', + 'gsiml' => '⪐', + 'GT' => '>', + 'G' => '>', + 'Gt' => '≫', + 'gt' => '>', + 'g' => '>', + 'gtcc' => '⪧', + 'gtcir' => '⩺', + 'gtdot' => '⋗', + 'gtlPar' => '⦕', + 'gtquest' => '⩼', + 'gtrapprox' => '⪆', + 'gtrarr' => '⥸', + 'gtrdot' => '⋗', + 'gtreqless' => '⋛', + 'gtreqqless' => '⪌', + 'gtrless' => '≷', + 'gtrsim' => '≳', + 'gvertneqq' => '≩︀', + 'gvnE' => '≩︀', + 'Hacek' => 'ˇ', + 'hairsp' => ' ', + 'half' => '½', + 'hamilt' => 'ℋ', + 'HARDcy' => 'Ъ', + 'hardcy' => 'ъ', + 'hArr' => '⇔', + 'harr' => '↔', + 'harrcir' => '⥈', + 'harrw' => '↭', + 'Hat' => '^', + 'hbar' => 'ℏ', + 'Hcirc' => 'Ĥ', + 'hcirc' => 'ĥ', + 'hearts' => '♥', + 'heartsuit' => '♥', + 'hellip' => '…', + 'hercon' => '⊹', + 'Hfr' => 'ℌ', + 'hfr' => '𝔥', + 'HilbertSpace' => 'ℋ', + 'hksearow' => '⤥', + 'hkswarow' => '⤦', + 'hoarr' => '⇿', + 'homtht' => '∻', + 'hookleftarrow' => '↩', + 'hookrightarrow' => '↪', + 'Hopf' => 'ℍ', + 'hopf' => '𝕙', + 'horbar' => '―', + 'HorizontalLine' => '─', + 'Hscr' => 'ℋ', + 'hscr' => '𝒽', + 'hslash' => 'ℏ', + 'Hstrok' => 'Ħ', + 'hstrok' => 'ħ', + 'HumpDownHump' => '≎', + 'HumpEqual' => '≏', + 'hybull' => '⁃', + 'hyphen' => '‐', + 'Iacute' => 'Í', + 'Iacut' => 'Í', + 'iacute' => 'í', + 'iacut' => 'í', + 'ic' => '⁣', + 'Icirc' => 'Î', + 'Icir' => 'Î', + 'icirc' => 'î', + 'icir' => 'î', + 'Icy' => 'И', + 'icy' => 'и', + 'Idot' => 'İ', + 'IEcy' => 'Е', + 'iecy' => 'е', + 'iexcl' => '¡', + 'iexc' => '¡', + 'iff' => '⇔', + 'Ifr' => 'ℑ', + 'ifr' => '𝔦', + 'Igrave' => 'Ì', + 'Igrav' => 'Ì', + 'igrave' => 'ì', + 'igrav' => 'ì', + 'ii' => 'ⅈ', + 'iiiint' => '⨌', + 'iiint' => '∭', + 'iinfin' => '⧜', + 'iiota' => '℩', + 'IJlig' => 'IJ', + 'ijlig' => 'ij', + 'Im' => 'ℑ', + 'Imacr' => 'Ī', + 'imacr' => 'ī', + 'image' => 'ℑ', + 'ImaginaryI' => 'ⅈ', + 'imagline' => 'ℐ', + 'imagpart' => 'ℑ', + 'imath' => 'ı', + 'imof' => '⊷', + 'imped' => 'Ƶ', + 'Implies' => '⇒', + 'in' => '∈', + 'incare' => '℅', + 'infin' => '∞', + 'infintie' => '⧝', + 'inodot' => 'ı', + 'Int' => '∬', + 'int' => '∫', + 'intcal' => '⊺', + 'integers' => 'ℤ', + 'Integral' => '∫', + 'intercal' => '⊺', + 'Intersection' => '⋂', + 'intlarhk' => '⨗', + 'intprod' => '⨼', + 'InvisibleComma' => '⁣', + 'InvisibleTimes' => '⁢', + 'IOcy' => 'Ё', + 'iocy' => 'ё', + 'Iogon' => 'Į', + 'iogon' => 'į', + 'Iopf' => '𝕀', + 'iopf' => '𝕚', + 'Iota' => 'Ι', + 'iota' => 'ι', + 'iprod' => '⨼', + 'iquest' => '¿', + 'iques' => '¿', + 'Iscr' => 'ℐ', + 'iscr' => '𝒾', + 'isin' => '∈', + 'isindot' => '⋵', + 'isinE' => '⋹', + 'isins' => '⋴', + 'isinsv' => '⋳', + 'isinv' => '∈', + 'it' => '⁢', + 'Itilde' => 'Ĩ', + 'itilde' => 'ĩ', + 'Iukcy' => 'І', + 'iukcy' => 'і', + 'Iuml' => 'Ï', + 'Ium' => 'Ï', + 'iuml' => 'ï', + 'ium' => 'ï', + 'Jcirc' => 'Ĵ', + 'jcirc' => 'ĵ', + 'Jcy' => 'Й', + 'jcy' => 'й', + 'Jfr' => '𝔍', + 'jfr' => '𝔧', + 'jmath' => 'ȷ', + 'Jopf' => '𝕁', + 'jopf' => '𝕛', + 'Jscr' => '𝒥', + 'jscr' => '𝒿', + 'Jsercy' => 'Ј', + 'jsercy' => 'ј', + 'Jukcy' => 'Є', + 'jukcy' => 'є', + 'Kappa' => 'Κ', + 'kappa' => 'κ', + 'kappav' => 'ϰ', + 'Kcedil' => 'Ķ', + 'kcedil' => 'ķ', + 'Kcy' => 'К', + 'kcy' => 'к', + 'Kfr' => '𝔎', + 'kfr' => '𝔨', + 'kgreen' => 'ĸ', + 'KHcy' => 'Х', + 'khcy' => 'х', + 'KJcy' => 'Ќ', + 'kjcy' => 'ќ', + 'Kopf' => '𝕂', + 'kopf' => '𝕜', + 'Kscr' => '𝒦', + 'kscr' => '𝓀', + 'lAarr' => '⇚', + 'Lacute' => 'Ĺ', + 'lacute' => 'ĺ', + 'laemptyv' => '⦴', + 'lagran' => 'ℒ', + 'Lambda' => 'Λ', + 'lambda' => 'λ', + 'Lang' => '⟪', + 'lang' => '⟨', + 'langd' => '⦑', + 'langle' => '⟨', + 'lap' => '⪅', + 'Laplacetrf' => 'ℒ', + 'laquo' => '«', + 'laqu' => '«', + 'Larr' => '↞', + 'lArr' => '⇐', + 'larr' => '←', + 'larrb' => '⇤', + 'larrbfs' => '⤟', + 'larrfs' => '⤝', + 'larrhk' => '↩', + 'larrlp' => '↫', + 'larrpl' => '⤹', + 'larrsim' => '⥳', + 'larrtl' => '↢', + 'lat' => '⪫', + 'lAtail' => '⤛', + 'latail' => '⤙', + 'late' => '⪭', + 'lates' => '⪭︀', + 'lBarr' => '⤎', + 'lbarr' => '⤌', + 'lbbrk' => '❲', + 'lbrace' => '{', + 'lbrack' => '[', + 'lbrke' => '⦋', + 'lbrksld' => '⦏', + 'lbrkslu' => '⦍', + 'Lcaron' => 'Ľ', + 'lcaron' => 'ľ', + 'Lcedil' => 'Ļ', + 'lcedil' => 'ļ', + 'lceil' => '⌈', + 'lcub' => '{', + 'Lcy' => 'Л', + 'lcy' => 'л', + 'ldca' => '⤶', + 'ldquo' => '“', + 'ldquor' => '„', + 'ldrdhar' => '⥧', + 'ldrushar' => '⥋', + 'ldsh' => '↲', + 'lE' => '≦', + 'le' => '≤', + 'LeftAngleBracket' => '⟨', + 'LeftArrow' => '←', + 'Leftarrow' => '⇐', + 'leftarrow' => '←', + 'LeftArrowBar' => '⇤', + 'LeftArrowRightArrow' => '⇆', + 'leftarrowtail' => '↢', + 'LeftCeiling' => '⌈', + 'LeftDoubleBracket' => '⟦', + 'LeftDownTeeVector' => '⥡', + 'LeftDownVector' => '⇃', + 'LeftDownVectorBar' => '⥙', + 'LeftFloor' => '⌊', + 'leftharpoondown' => '↽', + 'leftharpoonup' => '↼', + 'leftleftarrows' => '⇇', + 'LeftRightArrow' => '↔', + 'Leftrightarrow' => '⇔', + 'leftrightarrow' => '↔', + 'leftrightarrows' => '⇆', + 'leftrightharpoons' => '⇋', + 'leftrightsquigarrow' => '↭', + 'LeftRightVector' => '⥎', + 'LeftTee' => '⊣', + 'LeftTeeArrow' => '↤', + 'LeftTeeVector' => '⥚', + 'leftthreetimes' => '⋋', + 'LeftTriangle' => '⊲', + 'LeftTriangleBar' => '⧏', + 'LeftTriangleEqual' => '⊴', + 'LeftUpDownVector' => '⥑', + 'LeftUpTeeVector' => '⥠', + 'LeftUpVector' => '↿', + 'LeftUpVectorBar' => '⥘', + 'LeftVector' => '↼', + 'LeftVectorBar' => '⥒', + 'lEg' => '⪋', + 'leg' => '⋚', + 'leq' => '≤', + 'leqq' => '≦', + 'leqslant' => '⩽', + 'les' => '⩽', + 'lescc' => '⪨', + 'lesdot' => '⩿', + 'lesdoto' => '⪁', + 'lesdotor' => '⪃', + 'lesg' => '⋚︀', + 'lesges' => '⪓', + 'lessapprox' => '⪅', + 'lessdot' => '⋖', + 'lesseqgtr' => '⋚', + 'lesseqqgtr' => '⪋', + 'LessEqualGreater' => '⋚', + 'LessFullEqual' => '≦', + 'LessGreater' => '≶', + 'lessgtr' => '≶', + 'LessLess' => '⪡', + 'lesssim' => '≲', + 'LessSlantEqual' => '⩽', + 'LessTilde' => '≲', + 'lfisht' => '⥼', + 'lfloor' => '⌊', + 'Lfr' => '𝔏', + 'lfr' => '𝔩', + 'lg' => '≶', + 'lgE' => '⪑', + 'lHar' => '⥢', + 'lhard' => '↽', + 'lharu' => '↼', + 'lharul' => '⥪', + 'lhblk' => '▄', + 'LJcy' => 'Љ', + 'ljcy' => 'љ', + 'Ll' => '⋘', + 'll' => '≪', + 'llarr' => '⇇', + 'llcorner' => '⌞', + 'Lleftarrow' => '⇚', + 'llhard' => '⥫', + 'lltri' => '◺', + 'Lmidot' => 'Ŀ', + 'lmidot' => 'ŀ', + 'lmoust' => '⎰', + 'lmoustache' => '⎰', + 'lnap' => '⪉', + 'lnapprox' => '⪉', + 'lnE' => '≨', + 'lne' => '⪇', + 'lneq' => '⪇', + 'lneqq' => '≨', + 'lnsim' => '⋦', + 'loang' => '⟬', + 'loarr' => '⇽', + 'lobrk' => '⟦', + 'LongLeftArrow' => '⟵', + 'Longleftarrow' => '⟸', + 'longleftarrow' => '⟵', + 'LongLeftRightArrow' => '⟷', + 'Longleftrightarrow' => '⟺', + 'longleftrightarrow' => '⟷', + 'longmapsto' => '⟼', + 'LongRightArrow' => '⟶', + 'Longrightarrow' => '⟹', + 'longrightarrow' => '⟶', + 'looparrowleft' => '↫', + 'looparrowright' => '↬', + 'lopar' => '⦅', + 'Lopf' => '𝕃', + 'lopf' => '𝕝', + 'loplus' => '⨭', + 'lotimes' => '⨴', + 'lowast' => '∗', + 'lowbar' => '_', + 'LowerLeftArrow' => '↙', + 'LowerRightArrow' => '↘', + 'loz' => '◊', + 'lozenge' => '◊', + 'lozf' => '⧫', + 'lpar' => '(', + 'lparlt' => '⦓', + 'lrarr' => '⇆', + 'lrcorner' => '⌟', + 'lrhar' => '⇋', + 'lrhard' => '⥭', + 'lrm' => '‎', + 'lrtri' => '⊿', + 'lsaquo' => '‹', + 'Lscr' => 'ℒ', + 'lscr' => '𝓁', + 'Lsh' => '↰', + 'lsh' => '↰', + 'lsim' => '≲', + 'lsime' => '⪍', + 'lsimg' => '⪏', + 'lsqb' => '[', + 'lsquo' => '‘', + 'lsquor' => '‚', + 'Lstrok' => 'Ł', + 'lstrok' => 'ł', + 'LT' => '<', + 'L' => '<', + 'Lt' => '≪', + 'lt' => '<', + 'l' => '<', + 'ltcc' => '⪦', + 'ltcir' => '⩹', + 'ltdot' => '⋖', + 'lthree' => '⋋', + 'ltimes' => '⋉', + 'ltlarr' => '⥶', + 'ltquest' => '⩻', + 'ltri' => '◃', + 'ltrie' => '⊴', + 'ltrif' => '◂', + 'ltrPar' => '⦖', + 'lurdshar' => '⥊', + 'luruhar' => '⥦', + 'lvertneqq' => '≨︀', + 'lvnE' => '≨︀', + 'macr' => '¯', + 'mac' => '¯', + 'male' => '♂', + 'malt' => '✠', + 'maltese' => '✠', + 'Map' => '⤅', + 'map' => '↦', + 'mapsto' => '↦', + 'mapstodown' => '↧', + 'mapstoleft' => '↤', + 'mapstoup' => '↥', + 'marker' => '▮', + 'mcomma' => '⨩', + 'Mcy' => 'М', + 'mcy' => 'м', + 'mdash' => '—', + 'mDDot' => '∺', + 'measuredangle' => '∡', + 'MediumSpace' => ' ', + 'Mellintrf' => 'ℳ', + 'Mfr' => '𝔐', + 'mfr' => '𝔪', + 'mho' => '℧', + 'micro' => 'µ', + 'micr' => 'µ', + 'mid' => '∣', + 'midast' => '*', + 'midcir' => '⫰', + 'middot' => '·', + 'middo' => '·', + 'minus' => '−', + 'minusb' => '⊟', + 'minusd' => '∸', + 'minusdu' => '⨪', + 'MinusPlus' => '∓', + 'mlcp' => '⫛', + 'mldr' => '…', + 'mnplus' => '∓', + 'models' => '⊧', + 'Mopf' => '𝕄', + 'mopf' => '𝕞', + 'mp' => '∓', + 'Mscr' => 'ℳ', + 'mscr' => '𝓂', + 'mstpos' => '∾', + 'Mu' => 'Μ', + 'mu' => 'μ', + 'multimap' => '⊸', + 'mumap' => '⊸', + 'nabla' => '∇', + 'Nacute' => 'Ń', + 'nacute' => 'ń', + 'nang' => '∠⃒', + 'nap' => '≉', + 'napE' => '⩰̸', + 'napid' => '≋̸', + 'napos' => 'ʼn', + 'napprox' => '≉', + 'natur' => '♮', + 'natural' => '♮', + 'naturals' => 'ℕ', + 'nbsp' => ' ', + 'nbs' => ' ', + 'nbump' => '≎̸', + 'nbumpe' => '≏̸', + 'ncap' => '⩃', + 'Ncaron' => 'Ň', + 'ncaron' => 'ň', + 'Ncedil' => 'Ņ', + 'ncedil' => 'ņ', + 'ncong' => '≇', + 'ncongdot' => '⩭̸', + 'ncup' => '⩂', + 'Ncy' => 'Н', + 'ncy' => 'н', + 'ndash' => '–', + 'ne' => '≠', + 'nearhk' => '⤤', + 'neArr' => '⇗', + 'nearr' => '↗', + 'nearrow' => '↗', + 'nedot' => '≐̸', + 'NegativeMediumSpace' => '​', + 'NegativeThickSpace' => '​', + 'NegativeThinSpace' => '​', + 'NegativeVeryThinSpace' => '​', + 'nequiv' => '≢', + 'nesear' => '⤨', + 'nesim' => '≂̸', + 'NestedGreaterGreater' => '≫', + 'NestedLessLess' => '≪', + 'NewLine' => ' +', + 'nexist' => '∄', + 'nexists' => '∄', + 'Nfr' => '𝔑', + 'nfr' => '𝔫', + 'ngE' => '≧̸', + 'nge' => '≱', + 'ngeq' => '≱', + 'ngeqq' => '≧̸', + 'ngeqslant' => '⩾̸', + 'nges' => '⩾̸', + 'nGg' => '⋙̸', + 'ngsim' => '≵', + 'nGt' => '≫⃒', + 'ngt' => '≯', + 'ngtr' => '≯', + 'nGtv' => '≫̸', + 'nhArr' => '⇎', + 'nharr' => '↮', + 'nhpar' => '⫲', + 'ni' => '∋', + 'nis' => '⋼', + 'nisd' => '⋺', + 'niv' => '∋', + 'NJcy' => 'Њ', + 'njcy' => 'њ', + 'nlArr' => '⇍', + 'nlarr' => '↚', + 'nldr' => '‥', + 'nlE' => '≦̸', + 'nle' => '≰', + 'nLeftarrow' => '⇍', + 'nleftarrow' => '↚', + 'nLeftrightarrow' => '⇎', + 'nleftrightarrow' => '↮', + 'nleq' => '≰', + 'nleqq' => '≦̸', + 'nleqslant' => '⩽̸', + 'nles' => '⩽̸', + 'nless' => '≮', + 'nLl' => '⋘̸', + 'nlsim' => '≴', + 'nLt' => '≪⃒', + 'nlt' => '≮', + 'nltri' => '⋪', + 'nltrie' => '⋬', + 'nLtv' => '≪̸', + 'nmid' => '∤', + 'NoBreak' => '⁠', + 'NonBreakingSpace' => ' ', + 'Nopf' => 'ℕ', + 'nopf' => '𝕟', + 'Not' => '⫬', + 'not' => '¬', + 'no' => '¬', + 'NotCongruent' => '≢', + 'NotCupCap' => '≭', + 'NotDoubleVerticalBar' => '∦', + 'NotElement' => '∉', + 'NotEqual' => '≠', + 'NotEqualTilde' => '≂̸', + 'NotExists' => '∄', + 'NotGreater' => '≯', + 'NotGreaterEqual' => '≱', + 'NotGreaterFullEqual' => '≧̸', + 'NotGreaterGreater' => '≫̸', + 'NotGreaterLess' => '≹', + 'NotGreaterSlantEqual' => '⩾̸', + 'NotGreaterTilde' => '≵', + 'NotHumpDownHump' => '≎̸', + 'NotHumpEqual' => '≏̸', + 'notin' => '∉', + 'notindot' => '⋵̸', + 'notinE' => '⋹̸', + 'notinva' => '∉', + 'notinvb' => '⋷', + 'notinvc' => '⋶', + 'NotLeftTriangle' => '⋪', + 'NotLeftTriangleBar' => '⧏̸', + 'NotLeftTriangleEqual' => '⋬', + 'NotLess' => '≮', + 'NotLessEqual' => '≰', + 'NotLessGreater' => '≸', + 'NotLessLess' => '≪̸', + 'NotLessSlantEqual' => '⩽̸', + 'NotLessTilde' => '≴', + 'NotNestedGreaterGreater' => '⪢̸', + 'NotNestedLessLess' => '⪡̸', + 'notni' => '∌', + 'notniva' => '∌', + 'notnivb' => '⋾', + 'notnivc' => '⋽', + 'NotPrecedes' => '⊀', + 'NotPrecedesEqual' => '⪯̸', + 'NotPrecedesSlantEqual' => '⋠', + 'NotReverseElement' => '∌', + 'NotRightTriangle' => '⋫', + 'NotRightTriangleBar' => '⧐̸', + 'NotRightTriangleEqual' => '⋭', + 'NotSquareSubset' => '⊏̸', + 'NotSquareSubsetEqual' => '⋢', + 'NotSquareSuperset' => '⊐̸', + 'NotSquareSupersetEqual' => '⋣', + 'NotSubset' => '⊂⃒', + 'NotSubsetEqual' => '⊈', + 'NotSucceeds' => '⊁', + 'NotSucceedsEqual' => '⪰̸', + 'NotSucceedsSlantEqual' => '⋡', + 'NotSucceedsTilde' => '≿̸', + 'NotSuperset' => '⊃⃒', + 'NotSupersetEqual' => '⊉', + 'NotTilde' => '≁', + 'NotTildeEqual' => '≄', + 'NotTildeFullEqual' => '≇', + 'NotTildeTilde' => '≉', + 'NotVerticalBar' => '∤', + 'npar' => '∦', + 'nparallel' => '∦', + 'nparsl' => '⫽⃥', + 'npart' => '∂̸', + 'npolint' => '⨔', + 'npr' => '⊀', + 'nprcue' => '⋠', + 'npre' => '⪯̸', + 'nprec' => '⊀', + 'npreceq' => '⪯̸', + 'nrArr' => '⇏', + 'nrarr' => '↛', + 'nrarrc' => '⤳̸', + 'nrarrw' => '↝̸', + 'nRightarrow' => '⇏', + 'nrightarrow' => '↛', + 'nrtri' => '⋫', + 'nrtrie' => '⋭', + 'nsc' => '⊁', + 'nsccue' => '⋡', + 'nsce' => '⪰̸', + 'Nscr' => '𝒩', + 'nscr' => '𝓃', + 'nshortmid' => '∤', + 'nshortparallel' => '∦', + 'nsim' => '≁', + 'nsime' => '≄', + 'nsimeq' => '≄', + 'nsmid' => '∤', + 'nspar' => '∦', + 'nsqsube' => '⋢', + 'nsqsupe' => '⋣', + 'nsub' => '⊄', + 'nsubE' => '⫅̸', + 'nsube' => '⊈', + 'nsubset' => '⊂⃒', + 'nsubseteq' => '⊈', + 'nsubseteqq' => '⫅̸', + 'nsucc' => '⊁', + 'nsucceq' => '⪰̸', + 'nsup' => '⊅', + 'nsupE' => '⫆̸', + 'nsupe' => '⊉', + 'nsupset' => '⊃⃒', + 'nsupseteq' => '⊉', + 'nsupseteqq' => '⫆̸', + 'ntgl' => '≹', + 'Ntilde' => 'Ñ', + 'Ntild' => 'Ñ', + 'ntilde' => 'ñ', + 'ntild' => 'ñ', + 'ntlg' => '≸', + 'ntriangleleft' => '⋪', + 'ntrianglelefteq' => '⋬', + 'ntriangleright' => '⋫', + 'ntrianglerighteq' => '⋭', + 'Nu' => 'Ν', + 'nu' => 'ν', + 'num' => '#', + 'numero' => '№', + 'numsp' => ' ', + 'nvap' => '≍⃒', + 'nVDash' => '⊯', + 'nVdash' => '⊮', + 'nvDash' => '⊭', + 'nvdash' => '⊬', + 'nvge' => '≥⃒', + 'nvgt' => '>⃒', + 'nvHarr' => '⤄', + 'nvinfin' => '⧞', + 'nvlArr' => '⤂', + 'nvle' => '≤⃒', + 'nvlt' => '<⃒', + 'nvltrie' => '⊴⃒', + 'nvrArr' => '⤃', + 'nvrtrie' => '⊵⃒', + 'nvsim' => '∼⃒', + 'nwarhk' => '⤣', + 'nwArr' => '⇖', + 'nwarr' => '↖', + 'nwarrow' => '↖', + 'nwnear' => '⤧', + 'Oacute' => 'Ó', + 'Oacut' => 'Ó', + 'oacute' => 'ó', + 'oacut' => 'ó', + 'oast' => '⊛', + 'ocir' => 'ô', + 'Ocirc' => 'Ô', + 'Ocir' => 'Ô', + 'ocirc' => 'ô', + 'Ocy' => 'О', + 'ocy' => 'о', + 'odash' => '⊝', + 'Odblac' => 'Ő', + 'odblac' => 'ő', + 'odiv' => '⨸', + 'odot' => '⊙', + 'odsold' => '⦼', + 'OElig' => 'Œ', + 'oelig' => 'œ', + 'ofcir' => '⦿', + 'Ofr' => '𝔒', + 'ofr' => '𝔬', + 'ogon' => '˛', + 'Ograve' => 'Ò', + 'Ograv' => 'Ò', + 'ograve' => 'ò', + 'ograv' => 'ò', + 'ogt' => '⧁', + 'ohbar' => '⦵', + 'ohm' => 'Ω', + 'oint' => '∮', + 'olarr' => '↺', + 'olcir' => '⦾', + 'olcross' => '⦻', + 'oline' => '‾', + 'olt' => '⧀', + 'Omacr' => 'Ō', + 'omacr' => 'ō', + 'Omega' => 'Ω', + 'omega' => 'ω', + 'Omicron' => 'Ο', + 'omicron' => 'ο', + 'omid' => '⦶', + 'ominus' => '⊖', + 'Oopf' => '𝕆', + 'oopf' => '𝕠', + 'opar' => '⦷', + 'OpenCurlyDoubleQuote' => '“', + 'OpenCurlyQuote' => '‘', + 'operp' => '⦹', + 'oplus' => '⊕', + 'Or' => '⩔', + 'or' => '∨', + 'orarr' => '↻', + 'ord' => 'º', + 'order' => 'ℴ', + 'orderof' => 'ℴ', + 'ordf' => 'ª', + 'ordm' => 'º', + 'origof' => '⊶', + 'oror' => '⩖', + 'orslope' => '⩗', + 'orv' => '⩛', + 'oS' => 'Ⓢ', + 'Oscr' => '𝒪', + 'oscr' => 'ℴ', + 'Oslash' => 'Ø', + 'Oslas' => 'Ø', + 'oslash' => 'ø', + 'oslas' => 'ø', + 'osol' => '⊘', + 'Otilde' => 'Õ', + 'Otild' => 'Õ', + 'otilde' => 'õ', + 'otild' => 'õ', + 'Otimes' => '⨷', + 'otimes' => '⊗', + 'otimesas' => '⨶', + 'Ouml' => 'Ö', + 'Oum' => 'Ö', + 'ouml' => 'ö', + 'oum' => 'ö', + 'ovbar' => '⌽', + 'OverBar' => '‾', + 'OverBrace' => '⏞', + 'OverBracket' => '⎴', + 'OverParenthesis' => '⏜', + 'par' => '¶', + 'para' => '¶', + 'parallel' => '∥', + 'parsim' => '⫳', + 'parsl' => '⫽', + 'part' => '∂', + 'PartialD' => '∂', + 'Pcy' => 'П', + 'pcy' => 'п', + 'percnt' => '%', + 'period' => '.', + 'permil' => '‰', + 'perp' => '⊥', + 'pertenk' => '‱', + 'Pfr' => '𝔓', + 'pfr' => '𝔭', + 'Phi' => 'Φ', + 'phi' => 'φ', + 'phiv' => 'ϕ', + 'phmmat' => 'ℳ', + 'phone' => '☎', + 'Pi' => 'Π', + 'pi' => 'π', + 'pitchfork' => '⋔', + 'piv' => 'ϖ', + 'planck' => 'ℏ', + 'planckh' => 'ℎ', + 'plankv' => 'ℏ', + 'plus' => '+', + 'plusacir' => '⨣', + 'plusb' => '⊞', + 'pluscir' => '⨢', + 'plusdo' => '∔', + 'plusdu' => '⨥', + 'pluse' => '⩲', + 'PlusMinus' => '±', + 'plusmn' => '±', + 'plusm' => '±', + 'plussim' => '⨦', + 'plustwo' => '⨧', + 'pm' => '±', + 'Poincareplane' => 'ℌ', + 'pointint' => '⨕', + 'Popf' => 'ℙ', + 'popf' => '𝕡', + 'pound' => '£', + 'poun' => '£', + 'Pr' => '⪻', + 'pr' => '≺', + 'prap' => '⪷', + 'prcue' => '≼', + 'prE' => '⪳', + 'pre' => '⪯', + 'prec' => '≺', + 'precapprox' => '⪷', + 'preccurlyeq' => '≼', + 'Precedes' => '≺', + 'PrecedesEqual' => '⪯', + 'PrecedesSlantEqual' => '≼', + 'PrecedesTilde' => '≾', + 'preceq' => '⪯', + 'precnapprox' => '⪹', + 'precneqq' => '⪵', + 'precnsim' => '⋨', + 'precsim' => '≾', + 'Prime' => '″', + 'prime' => '′', + 'primes' => 'ℙ', + 'prnap' => '⪹', + 'prnE' => '⪵', + 'prnsim' => '⋨', + 'prod' => '∏', + 'Product' => '∏', + 'profalar' => '⌮', + 'profline' => '⌒', + 'profsurf' => '⌓', + 'prop' => '∝', + 'Proportion' => '∷', + 'Proportional' => '∝', + 'propto' => '∝', + 'prsim' => '≾', + 'prurel' => '⊰', + 'Pscr' => '𝒫', + 'pscr' => '𝓅', + 'Psi' => 'Ψ', + 'psi' => 'ψ', + 'puncsp' => ' ', + 'Qfr' => '𝔔', + 'qfr' => '𝔮', + 'qint' => '⨌', + 'Qopf' => 'ℚ', + 'qopf' => '𝕢', + 'qprime' => '⁗', + 'Qscr' => '𝒬', + 'qscr' => '𝓆', + 'quaternions' => 'ℍ', + 'quatint' => '⨖', + 'quest' => '?', + 'questeq' => '≟', + 'QUOT' => '"', + 'QUO' => '"', + 'quot' => '"', + 'quo' => '"', + 'rAarr' => '⇛', + 'race' => '∽̱', + 'Racute' => 'Ŕ', + 'racute' => 'ŕ', + 'radic' => '√', + 'raemptyv' => '⦳', + 'Rang' => '⟫', + 'rang' => '⟩', + 'rangd' => '⦒', + 'range' => '⦥', + 'rangle' => '⟩', + 'raquo' => '»', + 'raqu' => '»', + 'Rarr' => '↠', + 'rArr' => '⇒', + 'rarr' => '→', + 'rarrap' => '⥵', + 'rarrb' => '⇥', + 'rarrbfs' => '⤠', + 'rarrc' => '⤳', + 'rarrfs' => '⤞', + 'rarrhk' => '↪', + 'rarrlp' => '↬', + 'rarrpl' => '⥅', + 'rarrsim' => '⥴', + 'Rarrtl' => '⤖', + 'rarrtl' => '↣', + 'rarrw' => '↝', + 'rAtail' => '⤜', + 'ratail' => '⤚', + 'ratio' => '∶', + 'rationals' => 'ℚ', + 'RBarr' => '⤐', + 'rBarr' => '⤏', + 'rbarr' => '⤍', + 'rbbrk' => '❳', + 'rbrace' => '}', + 'rbrack' => ']', + 'rbrke' => '⦌', + 'rbrksld' => '⦎', + 'rbrkslu' => '⦐', + 'Rcaron' => 'Ř', + 'rcaron' => 'ř', + 'Rcedil' => 'Ŗ', + 'rcedil' => 'ŗ', + 'rceil' => '⌉', + 'rcub' => '}', + 'Rcy' => 'Р', + 'rcy' => 'р', + 'rdca' => '⤷', + 'rdldhar' => '⥩', + 'rdquo' => '”', + 'rdquor' => '”', + 'rdsh' => '↳', + 'Re' => 'ℜ', + 'real' => 'ℜ', + 'realine' => 'ℛ', + 'realpart' => 'ℜ', + 'reals' => 'ℝ', + 'rect' => '▭', + 'REG' => '®', + 'RE' => '®', + 'reg' => '®', + 're' => '®', + 'ReverseElement' => '∋', + 'ReverseEquilibrium' => '⇋', + 'ReverseUpEquilibrium' => '⥯', + 'rfisht' => '⥽', + 'rfloor' => '⌋', + 'Rfr' => 'ℜ', + 'rfr' => '𝔯', + 'rHar' => '⥤', + 'rhard' => '⇁', + 'rharu' => '⇀', + 'rharul' => '⥬', + 'Rho' => 'Ρ', + 'rho' => 'ρ', + 'rhov' => 'ϱ', + 'RightAngleBracket' => '⟩', + 'RightArrow' => '→', + 'Rightarrow' => '⇒', + 'rightarrow' => '→', + 'RightArrowBar' => '⇥', + 'RightArrowLeftArrow' => '⇄', + 'rightarrowtail' => '↣', + 'RightCeiling' => '⌉', + 'RightDoubleBracket' => '⟧', + 'RightDownTeeVector' => '⥝', + 'RightDownVector' => '⇂', + 'RightDownVectorBar' => '⥕', + 'RightFloor' => '⌋', + 'rightharpoondown' => '⇁', + 'rightharpoonup' => '⇀', + 'rightleftarrows' => '⇄', + 'rightleftharpoons' => '⇌', + 'rightrightarrows' => '⇉', + 'rightsquigarrow' => '↝', + 'RightTee' => '⊢', + 'RightTeeArrow' => '↦', + 'RightTeeVector' => '⥛', + 'rightthreetimes' => '⋌', + 'RightTriangle' => '⊳', + 'RightTriangleBar' => '⧐', + 'RightTriangleEqual' => '⊵', + 'RightUpDownVector' => '⥏', + 'RightUpTeeVector' => '⥜', + 'RightUpVector' => '↾', + 'RightUpVectorBar' => '⥔', + 'RightVector' => '⇀', + 'RightVectorBar' => '⥓', + 'ring' => '˚', + 'risingdotseq' => '≓', + 'rlarr' => '⇄', + 'rlhar' => '⇌', + 'rlm' => '‏', + 'rmoust' => '⎱', + 'rmoustache' => '⎱', + 'rnmid' => '⫮', + 'roang' => '⟭', + 'roarr' => '⇾', + 'robrk' => '⟧', + 'ropar' => '⦆', + 'Ropf' => 'ℝ', + 'ropf' => '𝕣', + 'roplus' => '⨮', + 'rotimes' => '⨵', + 'RoundImplies' => '⥰', + 'rpar' => ')', + 'rpargt' => '⦔', + 'rppolint' => '⨒', + 'rrarr' => '⇉', + 'Rrightarrow' => '⇛', + 'rsaquo' => '›', + 'Rscr' => 'ℛ', + 'rscr' => '𝓇', + 'Rsh' => '↱', + 'rsh' => '↱', + 'rsqb' => ']', + 'rsquo' => '’', + 'rsquor' => '’', + 'rthree' => '⋌', + 'rtimes' => '⋊', + 'rtri' => '▹', + 'rtrie' => '⊵', + 'rtrif' => '▸', + 'rtriltri' => '⧎', + 'RuleDelayed' => '⧴', + 'ruluhar' => '⥨', + 'rx' => '℞', + 'Sacute' => 'Ś', + 'sacute' => 'ś', + 'sbquo' => '‚', + 'Sc' => '⪼', + 'sc' => '≻', + 'scap' => '⪸', + 'Scaron' => 'Š', + 'scaron' => 'š', + 'sccue' => '≽', + 'scE' => '⪴', + 'sce' => '⪰', + 'Scedil' => 'Ş', + 'scedil' => 'ş', + 'Scirc' => 'Ŝ', + 'scirc' => 'ŝ', + 'scnap' => '⪺', + 'scnE' => '⪶', + 'scnsim' => '⋩', + 'scpolint' => '⨓', + 'scsim' => '≿', + 'Scy' => 'С', + 'scy' => 'с', + 'sdot' => '⋅', + 'sdotb' => '⊡', + 'sdote' => '⩦', + 'searhk' => '⤥', + 'seArr' => '⇘', + 'searr' => '↘', + 'searrow' => '↘', + 'sect' => '§', + 'sec' => '§', + 'semi' => ';', + 'seswar' => '⤩', + 'setminus' => '∖', + 'setmn' => '∖', + 'sext' => '✶', + 'Sfr' => '𝔖', + 'sfr' => '𝔰', + 'sfrown' => '⌢', + 'sharp' => '♯', + 'SHCHcy' => 'Щ', + 'shchcy' => 'щ', + 'SHcy' => 'Ш', + 'shcy' => 'ш', + 'ShortDownArrow' => '↓', + 'ShortLeftArrow' => '←', + 'shortmid' => '∣', + 'shortparallel' => '∥', + 'ShortRightArrow' => '→', + 'ShortUpArrow' => '↑', + 'shy' => '­', + 'sh' => '­', + 'Sigma' => 'Σ', + 'sigma' => 'σ', + 'sigmaf' => 'ς', + 'sigmav' => 'ς', + 'sim' => '∼', + 'simdot' => '⩪', + 'sime' => '≃', + 'simeq' => '≃', + 'simg' => '⪞', + 'simgE' => '⪠', + 'siml' => '⪝', + 'simlE' => '⪟', + 'simne' => '≆', + 'simplus' => '⨤', + 'simrarr' => '⥲', + 'slarr' => '←', + 'SmallCircle' => '∘', + 'smallsetminus' => '∖', + 'smashp' => '⨳', + 'smeparsl' => '⧤', + 'smid' => '∣', + 'smile' => '⌣', + 'smt' => '⪪', + 'smte' => '⪬', + 'smtes' => '⪬︀', + 'SOFTcy' => 'Ь', + 'softcy' => 'ь', + 'sol' => '/', + 'solb' => '⧄', + 'solbar' => '⌿', + 'Sopf' => '𝕊', + 'sopf' => '𝕤', + 'spades' => '♠', + 'spadesuit' => '♠', + 'spar' => '∥', + 'sqcap' => '⊓', + 'sqcaps' => '⊓︀', + 'sqcup' => '⊔', + 'sqcups' => '⊔︀', + 'Sqrt' => '√', + 'sqsub' => '⊏', + 'sqsube' => '⊑', + 'sqsubset' => '⊏', + 'sqsubseteq' => '⊑', + 'sqsup' => '⊐', + 'sqsupe' => '⊒', + 'sqsupset' => '⊐', + 'sqsupseteq' => '⊒', + 'squ' => '□', + 'Square' => '□', + 'square' => '□', + 'SquareIntersection' => '⊓', + 'SquareSubset' => '⊏', + 'SquareSubsetEqual' => '⊑', + 'SquareSuperset' => '⊐', + 'SquareSupersetEqual' => '⊒', + 'SquareUnion' => '⊔', + 'squarf' => '▪', + 'squf' => '▪', + 'srarr' => '→', + 'Sscr' => '𝒮', + 'sscr' => '𝓈', + 'ssetmn' => '∖', + 'ssmile' => '⌣', + 'sstarf' => '⋆', + 'Star' => '⋆', + 'star' => '☆', + 'starf' => '★', + 'straightepsilon' => 'ϵ', + 'straightphi' => 'ϕ', + 'strns' => '¯', + 'Sub' => '⋐', + 'sub' => '⊂', + 'subdot' => '⪽', + 'subE' => '⫅', + 'sube' => '⊆', + 'subedot' => '⫃', + 'submult' => '⫁', + 'subnE' => '⫋', + 'subne' => '⊊', + 'subplus' => '⪿', + 'subrarr' => '⥹', + 'Subset' => '⋐', + 'subset' => '⊂', + 'subseteq' => '⊆', + 'subseteqq' => '⫅', + 'SubsetEqual' => '⊆', + 'subsetneq' => '⊊', + 'subsetneqq' => '⫋', + 'subsim' => '⫇', + 'subsub' => '⫕', + 'subsup' => '⫓', + 'succ' => '≻', + 'succapprox' => '⪸', + 'succcurlyeq' => '≽', + 'Succeeds' => '≻', + 'SucceedsEqual' => '⪰', + 'SucceedsSlantEqual' => '≽', + 'SucceedsTilde' => '≿', + 'succeq' => '⪰', + 'succnapprox' => '⪺', + 'succneqq' => '⪶', + 'succnsim' => '⋩', + 'succsim' => '≿', + 'SuchThat' => '∋', + 'Sum' => '∑', + 'sum' => '∑', + 'sung' => '♪', + 'Sup' => '⋑', + 'sup' => '³', + 'sup1' => '¹', + 'sup2' => '²', + 'sup3' => '³', + 'supdot' => '⪾', + 'supdsub' => '⫘', + 'supE' => '⫆', + 'supe' => '⊇', + 'supedot' => '⫄', + 'Superset' => '⊃', + 'SupersetEqual' => '⊇', + 'suphsol' => '⟉', + 'suphsub' => '⫗', + 'suplarr' => '⥻', + 'supmult' => '⫂', + 'supnE' => '⫌', + 'supne' => '⊋', + 'supplus' => '⫀', + 'Supset' => '⋑', + 'supset' => '⊃', + 'supseteq' => '⊇', + 'supseteqq' => '⫆', + 'supsetneq' => '⊋', + 'supsetneqq' => '⫌', + 'supsim' => '⫈', + 'supsub' => '⫔', + 'supsup' => '⫖', + 'swarhk' => '⤦', + 'swArr' => '⇙', + 'swarr' => '↙', + 'swarrow' => '↙', + 'swnwar' => '⤪', + 'szlig' => 'ß', + 'szli' => 'ß', + 'Tab' => ' ', + 'target' => '⌖', + 'Tau' => 'Τ', + 'tau' => 'τ', + 'tbrk' => '⎴', + 'Tcaron' => 'Ť', + 'tcaron' => 'ť', + 'Tcedil' => 'Ţ', + 'tcedil' => 'ţ', + 'Tcy' => 'Т', + 'tcy' => 'т', + 'tdot' => '⃛', + 'telrec' => '⌕', + 'Tfr' => '𝔗', + 'tfr' => '𝔱', + 'there4' => '∴', + 'Therefore' => '∴', + 'therefore' => '∴', + 'Theta' => 'Θ', + 'theta' => 'θ', + 'thetasym' => 'ϑ', + 'thetav' => 'ϑ', + 'thickapprox' => '≈', + 'thicksim' => '∼', + 'ThickSpace' => '  ', + 'thinsp' => ' ', + 'ThinSpace' => ' ', + 'thkap' => '≈', + 'thksim' => '∼', + 'THORN' => 'Þ', + 'THOR' => 'Þ', + 'thorn' => 'þ', + 'thor' => 'þ', + 'Tilde' => '∼', + 'tilde' => '˜', + 'TildeEqual' => '≃', + 'TildeFullEqual' => '≅', + 'TildeTilde' => '≈', + 'times' => '×', + 'time' => '×', + 'timesb' => '⊠', + 'timesbar' => '⨱', + 'timesd' => '⨰', + 'tint' => '∭', + 'toea' => '⤨', + 'top' => '⊤', + 'topbot' => '⌶', + 'topcir' => '⫱', + 'Topf' => '𝕋', + 'topf' => '𝕥', + 'topfork' => '⫚', + 'tosa' => '⤩', + 'tprime' => '‴', + 'TRADE' => '™', + 'trade' => '™', + 'triangle' => '▵', + 'triangledown' => '▿', + 'triangleleft' => '◃', + 'trianglelefteq' => '⊴', + 'triangleq' => '≜', + 'triangleright' => '▹', + 'trianglerighteq' => '⊵', + 'tridot' => '◬', + 'trie' => '≜', + 'triminus' => '⨺', + 'TripleDot' => '⃛', + 'triplus' => '⨹', + 'trisb' => '⧍', + 'tritime' => '⨻', + 'trpezium' => '⏢', + 'Tscr' => '𝒯', + 'tscr' => '𝓉', + 'TScy' => 'Ц', + 'tscy' => 'ц', + 'TSHcy' => 'Ћ', + 'tshcy' => 'ћ', + 'Tstrok' => 'Ŧ', + 'tstrok' => 'ŧ', + 'twixt' => '≬', + 'twoheadleftarrow' => '↞', + 'twoheadrightarrow' => '↠', + 'Uacute' => 'Ú', + 'Uacut' => 'Ú', + 'uacute' => 'ú', + 'uacut' => 'ú', + 'Uarr' => '↟', + 'uArr' => '⇑', + 'uarr' => '↑', + 'Uarrocir' => '⥉', + 'Ubrcy' => 'Ў', + 'ubrcy' => 'ў', + 'Ubreve' => 'Ŭ', + 'ubreve' => 'ŭ', + 'Ucirc' => 'Û', + 'Ucir' => 'Û', + 'ucirc' => 'û', + 'ucir' => 'û', + 'Ucy' => 'У', + 'ucy' => 'у', + 'udarr' => '⇅', + 'Udblac' => 'Ű', + 'udblac' => 'ű', + 'udhar' => '⥮', + 'ufisht' => '⥾', + 'Ufr' => '𝔘', + 'ufr' => '𝔲', + 'Ugrave' => 'Ù', + 'Ugrav' => 'Ù', + 'ugrave' => 'ù', + 'ugrav' => 'ù', + 'uHar' => '⥣', + 'uharl' => '↿', + 'uharr' => '↾', + 'uhblk' => '▀', + 'ulcorn' => '⌜', + 'ulcorner' => '⌜', + 'ulcrop' => '⌏', + 'ultri' => '◸', + 'Umacr' => 'Ū', + 'umacr' => 'ū', + 'uml' => '¨', + 'um' => '¨', + 'UnderBar' => '_', + 'UnderBrace' => '⏟', + 'UnderBracket' => '⎵', + 'UnderParenthesis' => '⏝', + 'Union' => '⋃', + 'UnionPlus' => '⊎', + 'Uogon' => 'Ų', + 'uogon' => 'ų', + 'Uopf' => '𝕌', + 'uopf' => '𝕦', + 'UpArrow' => '↑', + 'Uparrow' => '⇑', + 'uparrow' => '↑', + 'UpArrowBar' => '⤒', + 'UpArrowDownArrow' => '⇅', + 'UpDownArrow' => '↕', + 'Updownarrow' => '⇕', + 'updownarrow' => '↕', + 'UpEquilibrium' => '⥮', + 'upharpoonleft' => '↿', + 'upharpoonright' => '↾', + 'uplus' => '⊎', + 'UpperLeftArrow' => '↖', + 'UpperRightArrow' => '↗', + 'Upsi' => 'ϒ', + 'upsi' => 'υ', + 'upsih' => 'ϒ', + 'Upsilon' => 'Υ', + 'upsilon' => 'υ', + 'UpTee' => '⊥', + 'UpTeeArrow' => '↥', + 'upuparrows' => '⇈', + 'urcorn' => '⌝', + 'urcorner' => '⌝', + 'urcrop' => '⌎', + 'Uring' => 'Ů', + 'uring' => 'ů', + 'urtri' => '◹', + 'Uscr' => '𝒰', + 'uscr' => '𝓊', + 'utdot' => '⋰', + 'Utilde' => 'Ũ', + 'utilde' => 'ũ', + 'utri' => '▵', + 'utrif' => '▴', + 'uuarr' => '⇈', + 'Uuml' => 'Ü', + 'Uum' => 'Ü', + 'uuml' => 'ü', + 'uum' => 'ü', + 'uwangle' => '⦧', + 'vangrt' => '⦜', + 'varepsilon' => 'ϵ', + 'varkappa' => 'ϰ', + 'varnothing' => '∅', + 'varphi' => 'ϕ', + 'varpi' => 'ϖ', + 'varpropto' => '∝', + 'vArr' => '⇕', + 'varr' => '↕', + 'varrho' => 'ϱ', + 'varsigma' => 'ς', + 'varsubsetneq' => '⊊︀', + 'varsubsetneqq' => '⫋︀', + 'varsupsetneq' => '⊋︀', + 'varsupsetneqq' => '⫌︀', + 'vartheta' => 'ϑ', + 'vartriangleleft' => '⊲', + 'vartriangleright' => '⊳', + 'Vbar' => '⫫', + 'vBar' => '⫨', + 'vBarv' => '⫩', + 'Vcy' => 'В', + 'vcy' => 'в', + 'VDash' => '⊫', + 'Vdash' => '⊩', + 'vDash' => '⊨', + 'vdash' => '⊢', + 'Vdashl' => '⫦', + 'Vee' => '⋁', + 'vee' => '∨', + 'veebar' => '⊻', + 'veeeq' => '≚', + 'vellip' => '⋮', + 'Verbar' => '‖', + 'verbar' => '|', + 'Vert' => '‖', + 'vert' => '|', + 'VerticalBar' => '∣', + 'VerticalLine' => '|', + 'VerticalSeparator' => '❘', + 'VerticalTilde' => '≀', + 'VeryThinSpace' => ' ', + 'Vfr' => '𝔙', + 'vfr' => '𝔳', + 'vltri' => '⊲', + 'vnsub' => '⊂⃒', + 'vnsup' => '⊃⃒', + 'Vopf' => '𝕍', + 'vopf' => '𝕧', + 'vprop' => '∝', + 'vrtri' => '⊳', + 'Vscr' => '𝒱', + 'vscr' => '𝓋', + 'vsubnE' => '⫋︀', + 'vsubne' => '⊊︀', + 'vsupnE' => '⫌︀', + 'vsupne' => '⊋︀', + 'Vvdash' => '⊪', + 'vzigzag' => '⦚', + 'Wcirc' => 'Ŵ', + 'wcirc' => 'ŵ', + 'wedbar' => '⩟', + 'Wedge' => '⋀', + 'wedge' => '∧', + 'wedgeq' => '≙', + 'weierp' => '℘', + 'Wfr' => '𝔚', + 'wfr' => '𝔴', + 'Wopf' => '𝕎', + 'wopf' => '𝕨', + 'wp' => '℘', + 'wr' => '≀', + 'wreath' => '≀', + 'Wscr' => '𝒲', + 'wscr' => '𝓌', + 'xcap' => '⋂', + 'xcirc' => '◯', + 'xcup' => '⋃', + 'xdtri' => '▽', + 'Xfr' => '𝔛', + 'xfr' => '𝔵', + 'xhArr' => '⟺', + 'xharr' => '⟷', + 'Xi' => 'Ξ', + 'xi' => 'ξ', + 'xlArr' => '⟸', + 'xlarr' => '⟵', + 'xmap' => '⟼', + 'xnis' => '⋻', + 'xodot' => '⨀', + 'Xopf' => '𝕏', + 'xopf' => '𝕩', + 'xoplus' => '⨁', + 'xotime' => '⨂', + 'xrArr' => '⟹', + 'xrarr' => '⟶', + 'Xscr' => '𝒳', + 'xscr' => '𝓍', + 'xsqcup' => '⨆', + 'xuplus' => '⨄', + 'xutri' => '△', + 'xvee' => '⋁', + 'xwedge' => '⋀', + 'Yacute' => 'Ý', + 'Yacut' => 'Ý', + 'yacute' => 'ý', + 'yacut' => 'ý', + 'YAcy' => 'Я', + 'yacy' => 'я', + 'Ycirc' => 'Ŷ', + 'ycirc' => 'ŷ', + 'Ycy' => 'Ы', + 'ycy' => 'ы', + 'yen' => '¥', + 'ye' => '¥', + 'Yfr' => '𝔜', + 'yfr' => '𝔶', + 'YIcy' => 'Ї', + 'yicy' => 'ї', + 'Yopf' => '𝕐', + 'yopf' => '𝕪', + 'Yscr' => '𝒴', + 'yscr' => '𝓎', + 'YUcy' => 'Ю', + 'yucy' => 'ю', + 'Yuml' => 'Ÿ', + 'yuml' => 'ÿ', + 'yum' => 'ÿ', + 'Zacute' => 'Ź', + 'zacute' => 'ź', + 'Zcaron' => 'Ž', + 'zcaron' => 'ž', + 'Zcy' => 'З', + 'zcy' => 'з', + 'Zdot' => 'Ż', + 'zdot' => 'ż', + 'zeetrf' => 'ℨ', + 'ZeroWidthSpace' => '​', + 'Zeta' => 'Ζ', + 'zeta' => 'ζ', + 'Zfr' => 'ℨ', + 'zfr' => '𝔷', + 'ZHcy' => 'Ж', + 'zhcy' => 'ж', + 'zigrarr' => '⇝', + 'Zopf' => 'ℤ', + 'zopf' => '𝕫', + 'Zscr' => '𝒵', + 'zscr' => '𝓏', + 'zwj' => '‍', + 'zwnj' => '‌', + ); +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Exception.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Exception.php new file mode 100644 index 000000000..64e97e6ff --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Exception.php @@ -0,0 +1,10 @@ + self::NAMESPACE_HTML, + 'svg' => self::NAMESPACE_SVG, + 'math' => self::NAMESPACE_MATHML, + ); + + /** + * Holds the always available namespaces (which does not require the XMLNS declaration). + * + * @var array + */ + protected $implicitNamespaces = array( + 'xml' => self::NAMESPACE_XML, + 'xmlns' => self::NAMESPACE_XMLNS, + 'xlink' => self::NAMESPACE_XLINK, + ); + + /** + * Holds a stack of currently active namespaces. + * + * @var array + */ + protected $nsStack = array(); + + /** + * Holds the number of namespaces declared by a node. + * + * @var array + */ + protected $pushes = array(); + + /** + * Defined in 8.2.5. + */ + const IM_INITIAL = 0; + + const IM_BEFORE_HTML = 1; + + const IM_BEFORE_HEAD = 2; + + const IM_IN_HEAD = 3; + + const IM_IN_HEAD_NOSCRIPT = 4; + + const IM_AFTER_HEAD = 5; + + const IM_IN_BODY = 6; + + const IM_TEXT = 7; + + const IM_IN_TABLE = 8; + + const IM_IN_TABLE_TEXT = 9; + + const IM_IN_CAPTION = 10; + + const IM_IN_COLUMN_GROUP = 11; + + const IM_IN_TABLE_BODY = 12; + + const IM_IN_ROW = 13; + + const IM_IN_CELL = 14; + + const IM_IN_SELECT = 15; + + const IM_IN_SELECT_IN_TABLE = 16; + + const IM_AFTER_BODY = 17; + + const IM_IN_FRAMESET = 18; + + const IM_AFTER_FRAMESET = 19; + + const IM_AFTER_AFTER_BODY = 20; + + const IM_AFTER_AFTER_FRAMESET = 21; + + const IM_IN_SVG = 22; + + const IM_IN_MATHML = 23; + + protected $options = array(); + + protected $stack = array(); + + protected $current; // Pointer in the tag hierarchy. + protected $rules; + protected $doc; + + protected $frag; + + protected $processor; + + protected $insertMode = 0; + + /** + * Track if we are in an element that allows only inline child nodes. + * + * @var string|null + */ + protected $onlyInline; + + /** + * Quirks mode is enabled by default. + * Any document that is missing the DT will be considered to be in quirks mode. + */ + protected $quirks = true; + + protected $errors = array(); + + public function __construct($isFragment = false, array $options = array()) + { + $this->options = $options; + + if (isset($options[self::OPT_TARGET_DOC])) { + $this->doc = $options[self::OPT_TARGET_DOC]; + } else { + $impl = new \DOMImplementation(); + // XXX: + // Create the doctype. For now, we are always creating HTML5 + // documents, and attempting to up-convert any older DTDs to HTML5. + $dt = $impl->createDocumentType('html'); + // $this->doc = \DOMImplementation::createDocument(NULL, 'html', $dt); + $this->doc = $impl->createDocument(null, '', $dt); + $this->doc->encoding = !empty($options['encoding']) ? $options['encoding'] : 'UTF-8'; + } + + $this->errors = array(); + + $this->current = $this->doc; // ->documentElement; + + // Create a rules engine for tags. + $this->rules = new TreeBuildingRules(); + + $implicitNS = array(); + if (isset($this->options[self::OPT_IMPLICIT_NS])) { + $implicitNS = $this->options[self::OPT_IMPLICIT_NS]; + } elseif (isset($this->options['implicitNamespaces'])) { + $implicitNS = $this->options['implicitNamespaces']; + } + + // Fill $nsStack with the defalut HTML5 namespaces, plus the "implicitNamespaces" array taken form $options + array_unshift($this->nsStack, $implicitNS + array('' => self::NAMESPACE_HTML) + $this->implicitNamespaces); + + if ($isFragment) { + $this->insertMode = static::IM_IN_BODY; + $this->frag = $this->doc->createDocumentFragment(); + $this->current = $this->frag; + } + } + + /** + * Get the document. + */ + public function document() + { + return $this->doc; + } + + /** + * Get the DOM fragment for the body. + * + * This returns a DOMNodeList because a fragment may have zero or more + * DOMNodes at its root. + * + * @see http://www.w3.org/TR/2012/CR-html5-20121217/syntax.html#concept-frag-parse-context + * + * @return \DOMDocumentFragment + */ + public function fragment() + { + return $this->frag; + } + + /** + * Provide an instruction processor. + * + * This is used for handling Processor Instructions as they are + * inserted. If omitted, PI's are inserted directly into the DOM tree. + * + * @param InstructionProcessor $proc + */ + public function setInstructionProcessor(InstructionProcessor $proc) + { + $this->processor = $proc; + } + + public function doctype($name, $idType = 0, $id = null, $quirks = false) + { + // This is used solely for setting quirks mode. Currently we don't + // try to preserve the inbound DT. We convert it to HTML5. + $this->quirks = $quirks; + + if ($this->insertMode > static::IM_INITIAL) { + $this->parseError('Illegal placement of DOCTYPE tag. Ignoring: ' . $name); + + return; + } + + $this->insertMode = static::IM_BEFORE_HTML; + } + + /** + * Process the start tag. + * + * @todo - XMLNS namespace handling (we need to parse, even if it's not valid) + * - XLink, MathML and SVG namespace handling + * - Omission rules: 8.1.2.4 Optional tags + * + * @param string $name + * @param array $attributes + * @param bool $selfClosing + * + * @return int + */ + public function startTag($name, $attributes = array(), $selfClosing = false) + { + $lname = $this->normalizeTagName($name); + + // Make sure we have an html element. + if (!$this->doc->documentElement && 'html' !== $name && !$this->frag) { + $this->startTag('html'); + } + + // Set quirks mode if we're at IM_INITIAL with no doctype. + if ($this->insertMode === static::IM_INITIAL) { + $this->quirks = true; + $this->parseError('No DOCTYPE specified.'); + } + + // SPECIAL TAG HANDLING: + // Spec says do this, and "don't ask." + // find the spec where this is defined... looks problematic + if ('image' === $name && !($this->insertMode === static::IM_IN_SVG || $this->insertMode === static::IM_IN_MATHML)) { + $name = 'img'; + } + + // Autoclose p tags where appropriate. + if ($this->insertMode >= static::IM_IN_BODY && Elements::isA($name, Elements::AUTOCLOSE_P)) { + $this->autoclose('p'); + } + + // Set insert mode: + switch ($name) { + case 'html': + $this->insertMode = static::IM_BEFORE_HEAD; + break; + case 'head': + if ($this->insertMode > static::IM_BEFORE_HEAD) { + $this->parseError('Unexpected head tag outside of head context.'); + } else { + $this->insertMode = static::IM_IN_HEAD; + } + break; + case 'body': + $this->insertMode = static::IM_IN_BODY; + break; + case 'svg': + $this->insertMode = static::IM_IN_SVG; + break; + case 'math': + $this->insertMode = static::IM_IN_MATHML; + break; + case 'noscript': + if ($this->insertMode === static::IM_IN_HEAD) { + $this->insertMode = static::IM_IN_HEAD_NOSCRIPT; + } + break; + } + + // Special case handling for SVG. + if ($this->insertMode === static::IM_IN_SVG) { + $lname = Elements::normalizeSvgElement($lname); + } + + $pushes = 0; + // when we found a tag thats appears inside $nsRoots, we have to switch the defalut namespace + if (isset($this->nsRoots[$lname]) && $this->nsStack[0][''] !== $this->nsRoots[$lname]) { + array_unshift($this->nsStack, array( + '' => $this->nsRoots[$lname], + ) + $this->nsStack[0]); + ++$pushes; + } + $needsWorkaround = false; + if (isset($this->options['xmlNamespaces']) && $this->options['xmlNamespaces']) { + // when xmlNamespaces is true a and we found a 'xmlns' or 'xmlns:*' attribute, we should add a new item to the $nsStack + foreach ($attributes as $aName => $aVal) { + if ('xmlns' === $aName) { + $needsWorkaround = $aVal; + array_unshift($this->nsStack, array( + '' => $aVal, + ) + $this->nsStack[0]); + ++$pushes; + } elseif ('xmlns' === (($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : '')) { + array_unshift($this->nsStack, array( + substr($aName, $pos + 1) => $aVal, + ) + $this->nsStack[0]); + ++$pushes; + } + } + } + + if ($this->onlyInline && Elements::isA($lname, Elements::BLOCK_TAG)) { + $this->autoclose($this->onlyInline); + $this->onlyInline = null; + } + + try { + $prefix = ($pos = strpos($lname, ':')) ? substr($lname, 0, $pos) : ''; + + if (false !== $needsWorkaround) { + $xml = "<$lname xmlns=\"$needsWorkaround\" " . (strlen($prefix) && isset($this->nsStack[0][$prefix]) ? ("xmlns:$prefix=\"" . $this->nsStack[0][$prefix] . '"') : '') . '/>'; + + $frag = new \DOMDocument('1.0', 'UTF-8'); + $frag->loadXML($xml); + + $ele = $this->doc->importNode($frag->documentElement, true); + } else { + if (!isset($this->nsStack[0][$prefix]) || ('' === $prefix && isset($this->options[self::OPT_DISABLE_HTML_NS]) && $this->options[self::OPT_DISABLE_HTML_NS])) { + $ele = $this->doc->createElement($lname); + } else { + $ele = $this->doc->createElementNS($this->nsStack[0][$prefix], $lname); + } + } + } catch (\DOMException $e) { + $this->parseError("Illegal tag name: <$lname>. Replaced with ."); + $ele = $this->doc->createElement('invalid'); + } + + if (Elements::isA($lname, Elements::BLOCK_ONLY_INLINE)) { + $this->onlyInline = $lname; + } + + // When we add some namespacess, we have to track them. Later, when "endElement" is invoked, we have to remove them. + // When we are on a void tag, we do not need to care about namesapce nesting. + if ($pushes > 0 && !Elements::isA($name, Elements::VOID_TAG)) { + // PHP tends to free the memory used by DOM, + // to avoid spl_object_hash collisions whe have to avoid garbage collection of $ele storing it into $pushes + // see https://bugs.php.net/bug.php?id=67459 + $this->pushes[spl_object_hash($ele)] = array($pushes, $ele); + } + + foreach ($attributes as $aName => $aVal) { + // xmlns attributes can't be set + if ('xmlns' === $aName) { + continue; + } + + if ($this->insertMode === static::IM_IN_SVG) { + $aName = Elements::normalizeSvgAttribute($aName); + } elseif ($this->insertMode === static::IM_IN_MATHML) { + $aName = Elements::normalizeMathMlAttribute($aName); + } + + $aVal = (string) $aVal; + + try { + $prefix = ($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : false; + + if ('xmlns' === $prefix) { + $ele->setAttributeNS(self::NAMESPACE_XMLNS, $aName, $aVal); + } elseif (false !== $prefix && isset($this->nsStack[0][$prefix])) { + $ele->setAttributeNS($this->nsStack[0][$prefix], $aName, $aVal); + } else { + $ele->setAttribute($aName, $aVal); + } + } catch (\DOMException $e) { + $this->parseError("Illegal attribute name for tag $name. Ignoring: $aName"); + continue; + } + + // This is necessary on a non-DTD schema, like HTML5. + if ('id' === $aName) { + $ele->setIdAttribute('id', true); + } + } + + if ($this->frag !== $this->current && $this->rules->hasRules($name)) { + // Some elements have special processing rules. Handle those separately. + $this->current = $this->rules->evaluate($ele, $this->current); + } else { + // Otherwise, it's a standard element. + $this->current->appendChild($ele); + + if (!Elements::isA($name, Elements::VOID_TAG)) { + $this->current = $ele; + } + + // Self-closing tags should only be respected on foreign elements + // (and are implied on void elements) + // See: https://www.w3.org/TR/html5/syntax.html#start-tags + if (Elements::isHtml5Element($name)) { + $selfClosing = false; + } + } + + // This is sort of a last-ditch attempt to correct for cases where no head/body + // elements are provided. + if ($this->insertMode <= static::IM_BEFORE_HEAD && 'head' !== $name && 'html' !== $name) { + $this->insertMode = static::IM_IN_BODY; + } + + // When we are on a void tag, we do not need to care about namesapce nesting, + // but we have to remove the namespaces pushed to $nsStack. + if ($pushes > 0 && Elements::isA($name, Elements::VOID_TAG)) { + // remove the namespaced definded by current node + for ($i = 0; $i < $pushes; ++$i) { + array_shift($this->nsStack); + } + } + + if ($selfClosing) { + $this->endTag($name); + } + + // Return the element mask, which the tokenizer can then use to set + // various processing rules. + return Elements::element($name); + } + + public function endTag($name) + { + $lname = $this->normalizeTagName($name); + + // Special case within 12.2.6.4.7: An end tag whose tag name is "br" should be treated as an opening tag + if ('br' === $name) { + $this->parseError('Closing tag encountered for void element br.'); + + $this->startTag('br'); + } + // Ignore closing tags for other unary elements. + elseif (Elements::isA($name, Elements::VOID_TAG)) { + return; + } + + if ($this->insertMode <= static::IM_BEFORE_HTML) { + // 8.2.5.4.2 + if (in_array($name, array( + 'html', + 'br', + 'head', + 'title', + ))) { + $this->startTag('html'); + $this->endTag($name); + $this->insertMode = static::IM_BEFORE_HEAD; + + return; + } + + // Ignore the tag. + $this->parseError('Illegal closing tag at global scope.'); + + return; + } + + // Special case handling for SVG. + if ($this->insertMode === static::IM_IN_SVG) { + $lname = Elements::normalizeSvgElement($lname); + } + + $cid = spl_object_hash($this->current); + + // XXX: HTML has no parent. What do we do, though, + // if this element appears in the wrong place? + if ('html' === $lname) { + return; + } + + // remove the namespaced definded by current node + if (isset($this->pushes[$cid])) { + for ($i = 0; $i < $this->pushes[$cid][0]; ++$i) { + array_shift($this->nsStack); + } + unset($this->pushes[$cid]); + } + + if (!$this->autoclose($lname)) { + $this->parseError('Could not find closing tag for ' . $lname); + } + + switch ($lname) { + case 'head': + $this->insertMode = static::IM_AFTER_HEAD; + break; + case 'body': + $this->insertMode = static::IM_AFTER_BODY; + break; + case 'svg': + case 'mathml': + $this->insertMode = static::IM_IN_BODY; + break; + } + } + + public function comment($cdata) + { + // TODO: Need to handle case where comment appears outside of the HTML tag. + $node = $this->doc->createComment($cdata); + $this->current->appendChild($node); + } + + public function text($data) + { + // XXX: Hmmm.... should we really be this strict? + if ($this->insertMode < static::IM_IN_HEAD) { + // Per '8.2.5.4.3 The "before head" insertion mode' the characters + // " \t\n\r\f" should be ignored but no mention of a parse error. This is + // practical as most documents contain these characters. Other text is not + // expected here so recording a parse error is necessary. + $dataTmp = trim($data, " \t\n\r\f"); + if (!empty($dataTmp)) { + // fprintf(STDOUT, "Unexpected insert mode: %d", $this->insertMode); + $this->parseError('Unexpected text. Ignoring: ' . $dataTmp); + } + + return; + } + // fprintf(STDOUT, "Appending text %s.", $data); + $node = $this->doc->createTextNode($data); + $this->current->appendChild($node); + } + + public function eof() + { + // If the $current isn't the $root, do we need to do anything? + } + + public function parseError($msg, $line = 0, $col = 0) + { + $this->errors[] = sprintf('Line %d, Col %d: %s', $line, $col, $msg); + } + + public function getErrors() + { + return $this->errors; + } + + public function cdata($data) + { + $node = $this->doc->createCDATASection($data); + $this->current->appendChild($node); + } + + public function processingInstruction($name, $data = null) + { + // XXX: Ignore initial XML declaration, per the spec. + if ($this->insertMode === static::IM_INITIAL && 'xml' === strtolower($name)) { + return; + } + + // Important: The processor may modify the current DOM tree however it sees fit. + if ($this->processor instanceof InstructionProcessor) { + $res = $this->processor->process($this->current, $name, $data); + if (!empty($res)) { + $this->current = $res; + } + + return; + } + + // Otherwise, this is just a dumb PI element. + $node = $this->doc->createProcessingInstruction($name, $data); + + $this->current->appendChild($node); + } + + // ========================================================================== + // UTILITIES + // ========================================================================== + + /** + * Apply normalization rules to a tag name. + * See sections 2.9 and 8.1.2. + * + * @param string $tagName + * + * @return string The normalized tag name. + */ + protected function normalizeTagName($tagName) + { + /* + * Section 2.9 suggests that we should not do this. if (strpos($name, ':') !== false) { // We know from the grammar that there must be at least one other // char besides :, since : is not a legal tag start. $parts = explode(':', $name); return array_pop($parts); } + */ + return $tagName; + } + + protected function quirksTreeResolver($name) + { + throw new \Exception('Not implemented.'); + } + + /** + * Automatically climb the tree and close the closest node with the matching $tag. + * + * @param string $tagName + * + * @return bool + */ + protected function autoclose($tagName) + { + $working = $this->current; + do { + if (XML_ELEMENT_NODE !== $working->nodeType) { + return false; + } + if ($working->tagName === $tagName) { + $this->current = $working->parentNode; + + return true; + } + } while ($working = $working->parentNode); + + return false; + } + + /** + * Checks if the given tagname is an ancestor of the present candidate. + * + * If $this->current or anything above $this->current matches the given tag + * name, this returns true. + * + * @param string $tagName + * + * @return bool + */ + protected function isAncestor($tagName) + { + $candidate = $this->current; + while (XML_ELEMENT_NODE === $candidate->nodeType) { + if ($candidate->tagName === $tagName) { + return true; + } + $candidate = $candidate->parentNode; + } + + return false; + } + + /** + * Returns true if the immediate parent element is of the given tagname. + * + * @param string $tagName + * + * @return bool + */ + protected function isParent($tagName) + { + return $this->current->tagName === $tagName; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/EventHandler.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/EventHandler.php new file mode 100644 index 000000000..9893a718b --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/EventHandler.php @@ -0,0 +1,114 @@ +). + * + * @return int one of the Tokenizer::TEXTMODE_* constants + */ + public function startTag($name, $attributes = array(), $selfClosing = false); + + /** + * An end-tag. + */ + public function endTag($name); + + /** + * A comment section (unparsed character data). + */ + public function comment($cdata); + + /** + * A unit of parsed character data. + * + * Entities in this text are *already decoded*. + */ + public function text($cdata); + + /** + * Indicates that the document has been entirely processed. + */ + public function eof(); + + /** + * Emitted when the parser encounters an error condition. + */ + public function parseError($msg, $line, $col); + + /** + * A CDATA section. + * + * @param string $data + * The unparsed character data + */ + public function cdata($data); + + /** + * This is a holdover from the XML spec. + * + * While user agents don't get PIs, server-side does. + * + * @param string $name The name of the processor (e.g. 'php'). + * @param string $data The unparsed data. + */ + public function processingInstruction($name, $data = null); +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/FileInputStream.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/FileInputStream.php new file mode 100644 index 000000000..b081ed96b --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/FileInputStream.php @@ -0,0 +1,33 @@ +errors = UTF8Utils::checkForIllegalCodepoints($data); + + $data = $this->replaceLinefeeds($data); + + $this->data = $data; + $this->char = 0; + $this->EOF = strlen($data); + } + + /** + * Check if upcomming chars match the given sequence. + * + * This will read the stream for the $sequence. If it's + * found, this will return true. If not, return false. + * Since this unconsumes any chars it reads, the caller + * will still need to read the next sequence, even if + * this returns true. + * + * Example: $this->scanner->sequenceMatches('') will + * see if the input stream is at the start of a + * '' string. + * + * @param string $sequence + * @param bool $caseSensitive + * + * @return bool + */ + public function sequenceMatches($sequence, $caseSensitive = true) + { + $portion = substr($this->data, $this->char, strlen($sequence)); + + return $caseSensitive ? $portion === $sequence : 0 === strcasecmp($portion, $sequence); + } + + /** + * Get the current position. + * + * @return int The current intiger byte position. + */ + public function position() + { + return $this->char; + } + + /** + * Take a peek at the next character in the data. + * + * @return string The next character. + */ + public function peek() + { + if (($this->char + 1) < $this->EOF) { + return $this->data[$this->char + 1]; + } + + return false; + } + + /** + * Get the next character. + * Note: This advances the pointer. + * + * @return string The next character. + */ + public function next() + { + ++$this->char; + + if ($this->char < $this->EOF) { + return $this->data[$this->char]; + } + + return false; + } + + /** + * Get the current character. + * Note, this does not advance the pointer. + * + * @return string The current character. + */ + public function current() + { + if ($this->char < $this->EOF) { + return $this->data[$this->char]; + } + + return false; + } + + /** + * Silently consume N chars. + * + * @param int $count + */ + public function consume($count = 1) + { + $this->char += $count; + } + + /** + * Unconsume some of the data. + * This moves the data pointer backwards. + * + * @param int $howMany The number of characters to move the pointer back. + */ + public function unconsume($howMany = 1) + { + if (($this->char - $howMany) >= 0) { + $this->char -= $howMany; + } + } + + /** + * Get the next group of that contains hex characters. + * Note, along with getting the characters the pointer in the data will be + * moved as well. + * + * @return string The next group that is hex characters. + */ + public function getHex() + { + return $this->doCharsWhile(static::CHARS_HEX); + } + + /** + * Get the next group of characters that are ASCII Alpha characters. + * Note, along with getting the characters the pointer in the data will be + * moved as well. + * + * @return string The next group of ASCII alpha characters. + */ + public function getAsciiAlpha() + { + return $this->doCharsWhile(static::CHARS_ALPHA); + } + + /** + * Get the next group of characters that are ASCII Alpha characters and numbers. + * Note, along with getting the characters the pointer in the data will be + * moved as well. + * + * @return string The next group of ASCII alpha characters and numbers. + */ + public function getAsciiAlphaNum() + { + return $this->doCharsWhile(static::CHARS_ALNUM); + } + + /** + * Get the next group of numbers. + * Note, along with getting the characters the pointer in the data will be + * moved as well. + * + * @return string The next group of numbers. + */ + public function getNumeric() + { + return $this->doCharsWhile('0123456789'); + } + + /** + * Consume whitespace. + * Whitespace in HTML5 is: formfeed, tab, newline, space. + * + * @return int The length of the matched whitespaces. + */ + public function whitespace() + { + if ($this->char >= $this->EOF) { + return false; + } + + $len = strspn($this->data, "\n\t\f ", $this->char); + + $this->char += $len; + + return $len; + } + + /** + * Returns the current line that is being consumed. + * + * @return int The current line number. + */ + public function currentLine() + { + if (empty($this->EOF) || 0 === $this->char) { + return 1; + } + + // Add one to $this->char because we want the number for the next + // byte to be processed. + return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1; + } + + /** + * Read chars until something in the mask is encountered. + * + * @param string $mask + * + * @return mixed + */ + public function charsUntil($mask) + { + return $this->doCharsUntil($mask); + } + + /** + * Read chars as long as the mask matches. + * + * @param string $mask + * + * @return int + */ + public function charsWhile($mask) + { + return $this->doCharsWhile($mask); + } + + /** + * Returns the current column of the current line that the tokenizer is at. + * + * Newlines are column 0. The first char after a newline is column 1. + * + * @return int The column number. + */ + public function columnOffset() + { + // Short circuit for the first char. + if (0 === $this->char) { + return 0; + } + + // strrpos is weird, and the offset needs to be negative for what we + // want (i.e., the last \n before $this->char). This needs to not have + // one (to make it point to the next character, the one we want the + // position of) added to it because strrpos's behaviour includes the + // final offset byte. + $backwardFrom = $this->char - 1 - strlen($this->data); + $lastLine = strrpos($this->data, "\n", $backwardFrom); + + // However, for here we want the length up until the next byte to be + // processed, so add one to the current byte ($this->char). + if (false !== $lastLine) { + $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine); + } else { + // After a newline. + $findLengthOf = substr($this->data, 0, $this->char); + } + + return UTF8Utils::countChars($findLengthOf); + } + + /** + * Get all characters until EOF. + * + * This consumes characters until the EOF. + * + * @return int The number of characters remaining. + */ + public function remainingChars() + { + if ($this->char < $this->EOF) { + $data = substr($this->data, $this->char); + $this->char = $this->EOF; + + return $data; + } + + return ''; // false; + } + + /** + * Replace linefeed characters according to the spec. + * + * @param $data + * + * @return string + */ + private function replaceLinefeeds($data) + { + /* + * U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED (LF) characters are treated specially. + * Any CR characters that are followed by LF characters must be removed, and any CR characters not + * followed by LF characters must be converted to LF characters. Thus, newlines in HTML DOMs are + * represented by LF characters, and there are never any CR characters in the input to the tokenization + * stage. + */ + $crlfTable = array( + "\0" => "\xEF\xBF\xBD", + "\r\n" => "\n", + "\r" => "\n", + ); + + return strtr($data, $crlfTable); + } + + /** + * Read to a particular match (or until $max bytes are consumed). + * + * This operates on byte sequences, not characters. + * + * Matches as far as possible until we reach a certain set of bytes + * and returns the matched substring. + * + * @param string $bytes Bytes to match. + * @param int $max Maximum number of bytes to scan. + * + * @return mixed Index or false if no match is found. You should use strong + * equality when checking the result, since index could be 0. + */ + private function doCharsUntil($bytes, $max = null) + { + if ($this->char >= $this->EOF) { + return false; + } + + if (0 === $max || $max) { + $len = strcspn($this->data, $bytes, $this->char, $max); + } else { + $len = strcspn($this->data, $bytes, $this->char); + } + + $string = (string) substr($this->data, $this->char, $len); + $this->char += $len; + + return $string; + } + + /** + * Returns the string so long as $bytes matches. + * + * Matches as far as possible with a certain set of bytes + * and returns the matched substring. + * + * @param string $bytes A mask of bytes to match. If ANY byte in this mask matches the + * current char, the pointer advances and the char is part of the + * substring. + * @param int $max The max number of chars to read. + * + * @return string + */ + private function doCharsWhile($bytes, $max = null) + { + if ($this->char >= $this->EOF) { + return false; + } + + if (0 === $max || $max) { + $len = strspn($this->data, $bytes, $this->char, $max); + } else { + $len = strspn($this->data, $bytes, $this->char); + } + + $string = (string) substr($this->data, $this->char, $len); + $this->char += $len; + + return $string; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/StringInputStream.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/StringInputStream.php new file mode 100644 index 000000000..0c213feb6 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/StringInputStream.php @@ -0,0 +1,331 @@ + + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +// Some conventions: +// - /* */ indicates verbatim text from the HTML 5 specification +// MPB: Not sure which version of the spec. Moving from HTML5lib to +// HTML5-PHP, I have been using this version: +// http://www.w3.org/TR/2012/CR-html5-20121217/Overview.html#contents +// +// - // indicates regular comments + +/** + * @deprecated since 2.4, to remove in 3.0. Use a string in the scanner instead. + */ +class StringInputStream implements InputStream +{ + /** + * The string data we're parsing. + */ + private $data; + + /** + * The current integer byte position we are in $data. + */ + private $char; + + /** + * Length of $data; when $char === $data, we are at the end-of-file. + */ + private $EOF; + + /** + * Parse errors. + */ + public $errors = array(); + + /** + * Create a new InputStream wrapper. + * + * @param string $data Data to parse. + * @param string $encoding The encoding to use for the data. + * @param string $debug A fprintf format to use to echo the data on stdout. + */ + public function __construct($data, $encoding = 'UTF-8', $debug = '') + { + $data = UTF8Utils::convertToUTF8($data, $encoding); + if ($debug) { + fprintf(STDOUT, $debug, $data, strlen($data)); + } + + // There is good reason to question whether it makes sense to + // do this here, since most of these checks are done during + // parsing, and since this check doesn't actually *do* anything. + $this->errors = UTF8Utils::checkForIllegalCodepoints($data); + + $data = $this->replaceLinefeeds($data); + + $this->data = $data; + $this->char = 0; + $this->EOF = strlen($data); + } + + public function __toString() + { + return $this->data; + } + + /** + * Replace linefeed characters according to the spec. + */ + protected function replaceLinefeeds($data) + { + /* + * U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED (LF) characters are treated specially. + * Any CR characters that are followed by LF characters must be removed, and any CR characters not + * followed by LF characters must be converted to LF characters. Thus, newlines in HTML DOMs are + * represented by LF characters, and there are never any CR characters in the input to the tokenization + * stage. + */ + $crlfTable = array( + "\0" => "\xEF\xBF\xBD", + "\r\n" => "\n", + "\r" => "\n", + ); + + return strtr($data, $crlfTable); + } + + /** + * Returns the current line that the tokenizer is at. + */ + public function currentLine() + { + if (empty($this->EOF) || 0 === $this->char) { + return 1; + } + // Add one to $this->char because we want the number for the next + // byte to be processed. + return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1; + } + + /** + * @deprecated + */ + public function getCurrentLine() + { + return $this->currentLine(); + } + + /** + * Returns the current column of the current line that the tokenizer is at. + * Newlines are column 0. The first char after a newline is column 1. + * + * @return int The column number. + */ + public function columnOffset() + { + // Short circuit for the first char. + if (0 === $this->char) { + return 0; + } + // strrpos is weird, and the offset needs to be negative for what we + // want (i.e., the last \n before $this->char). This needs to not have + // one (to make it point to the next character, the one we want the + // position of) added to it because strrpos's behaviour includes the + // final offset byte. + $backwardFrom = $this->char - 1 - strlen($this->data); + $lastLine = strrpos($this->data, "\n", $backwardFrom); + + // However, for here we want the length up until the next byte to be + // processed, so add one to the current byte ($this->char). + if (false !== $lastLine) { + $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine); + } else { + // After a newline. + $findLengthOf = substr($this->data, 0, $this->char); + } + + return UTF8Utils::countChars($findLengthOf); + } + + /** + * @deprecated + */ + public function getColumnOffset() + { + return $this->columnOffset(); + } + + /** + * Get the current character. + * + * @return string The current character. + */ + public function current() + { + return $this->data[$this->char]; + } + + /** + * Advance the pointer. + * This is part of the Iterator interface. + */ + public function next() + { + ++$this->char; + } + + /** + * Rewind to the start of the string. + */ + public function rewind() + { + $this->char = 0; + } + + /** + * Is the current pointer location valid. + * + * @return bool Whether the current pointer location is valid. + */ + public function valid() + { + return $this->char < $this->EOF; + } + + /** + * Get all characters until EOF. + * + * This reads to the end of the file, and sets the read marker at the + * end of the file. + * + * Note this performs bounds checking. + * + * @return string Returns the remaining text. If called when the InputStream is + * already exhausted, it returns an empty string. + */ + public function remainingChars() + { + if ($this->char < $this->EOF) { + $data = substr($this->data, $this->char); + $this->char = $this->EOF; + + return $data; + } + + return ''; // false; + } + + /** + * Read to a particular match (or until $max bytes are consumed). + * + * This operates on byte sequences, not characters. + * + * Matches as far as possible until we reach a certain set of bytes + * and returns the matched substring. + * + * @param string $bytes Bytes to match. + * @param int $max Maximum number of bytes to scan. + * + * @return mixed Index or false if no match is found. You should use strong + * equality when checking the result, since index could be 0. + */ + public function charsUntil($bytes, $max = null) + { + if ($this->char >= $this->EOF) { + return false; + } + + if (0 === $max || $max) { + $len = strcspn($this->data, $bytes, $this->char, $max); + } else { + $len = strcspn($this->data, $bytes, $this->char); + } + + $string = (string) substr($this->data, $this->char, $len); + $this->char += $len; + + return $string; + } + + /** + * Returns the string so long as $bytes matches. + * + * Matches as far as possible with a certain set of bytes + * and returns the matched substring. + * + * @param string $bytes A mask of bytes to match. If ANY byte in this mask matches the + * current char, the pointer advances and the char is part of the + * substring. + * @param int $max The max number of chars to read. + * + * @return string + */ + public function charsWhile($bytes, $max = null) + { + if ($this->char >= $this->EOF) { + return false; + } + + if (0 === $max || $max) { + $len = strspn($this->data, $bytes, $this->char, $max); + } else { + $len = strspn($this->data, $bytes, $this->char); + } + $string = (string) substr($this->data, $this->char, $len); + $this->char += $len; + + return $string; + } + + /** + * Unconsume characters. + * + * @param int $howMany The number of characters to unconsume. + */ + public function unconsume($howMany = 1) + { + if (($this->char - $howMany) >= 0) { + $this->char -= $howMany; + } + } + + /** + * Look ahead without moving cursor. + */ + public function peek() + { + if (($this->char + 1) <= $this->EOF) { + return $this->data[$this->char + 1]; + } + + return false; + } + + public function key() + { + return $this->char; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Tokenizer.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Tokenizer.php new file mode 100644 index 000000000..016919ae2 --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/Tokenizer.php @@ -0,0 +1,1197 @@ +scanner = $scanner; + $this->events = $eventHandler; + $this->mode = $mode; + } + + /** + * Begin parsing. + * + * This will begin scanning the document, tokenizing as it goes. + * Tokens are emitted into the event handler. + * + * Tokenizing will continue until the document is completely + * read. Errors are emitted into the event handler, but + * the parser will attempt to continue parsing until the + * entire input stream is read. + */ + public function parse() + { + do { + $this->consumeData(); + // FIXME: Add infinite loop protection. + } while ($this->carryOn); + } + + /** + * Set the text mode for the character data reader. + * + * HTML5 defines three different modes for reading text: + * - Normal: Read until a tag is encountered. + * - RCDATA: Read until a tag is encountered, but skip a few otherwise- + * special characters. + * - Raw: Read until a special closing tag is encountered (viz. pre, script) + * + * This allows those modes to be set. + * + * Normally, setting is done by the event handler via a special return code on + * startTag(), but it can also be set manually using this function. + * + * @param int $textmode One of Elements::TEXT_*. + * @param string $untilTag The tag that should stop RAW or RCDATA mode. Normal mode does not + * use this indicator. + */ + public function setTextMode($textmode, $untilTag = null) + { + $this->textMode = $textmode & (Elements::TEXT_RAW | Elements::TEXT_RCDATA); + $this->untilTag = $untilTag; + } + + /** + * Consume a character and make a move. + * HTML5 8.2.4.1. + */ + protected function consumeData() + { + $tok = $this->scanner->current(); + + if ('&' === $tok) { + // Character reference + $ref = $this->decodeCharacterReference(); + $this->buffer($ref); + + $tok = $this->scanner->current(); + } + + // Parse tag + if ('<' === $tok) { + // Any buffered text data can go out now. + $this->flushBuffer(); + + $tok = $this->scanner->next(); + + if ('!' === $tok) { + $this->markupDeclaration(); + } elseif ('/' === $tok) { + $this->endTag(); + } elseif ('?' === $tok) { + $this->processingInstruction(); + } elseif (ctype_alpha($tok)) { + $this->tagName(); + } else { + $this->parseError('Illegal tag opening'); + // TODO is this necessary ? + $this->characterData(); + } + + $tok = $this->scanner->current(); + } + + if (false === $tok) { + // Handle end of document + $this->eof(); + } else { + // Parse character + switch ($this->textMode) { + case Elements::TEXT_RAW: + $this->rawText($tok); + break; + + case Elements::TEXT_RCDATA: + $this->rcdata($tok); + break; + + default: + if ('<' === $tok || '&' === $tok) { + break; + } + + // NULL character + if ("\00" === $tok) { + $this->parseError('Received null character.'); + + $this->text .= $tok; + $this->scanner->consume(); + + break; + } + + $this->text .= $this->scanner->charsUntil("<&\0"); + } + } + + return $this->carryOn; + } + + /** + * Parse anything that looks like character data. + * + * Different rules apply based on the current text mode. + * + * @see Elements::TEXT_RAW Elements::TEXT_RCDATA. + */ + protected function characterData() + { + $tok = $this->scanner->current(); + if (false === $tok) { + return false; + } + switch ($this->textMode) { + case Elements::TEXT_RAW: + return $this->rawText($tok); + case Elements::TEXT_RCDATA: + return $this->rcdata($tok); + default: + if ('<' === $tok || '&' === $tok) { + return false; + } + + return $this->text($tok); + } + } + + /** + * This buffers the current token as character data. + * + * @param string $tok The current token. + * + * @return bool + */ + protected function text($tok) + { + // This should never happen... + if (false === $tok) { + return false; + } + + // NULL character + if ("\00" === $tok) { + $this->parseError('Received null character.'); + } + + $this->buffer($tok); + $this->scanner->consume(); + + return true; + } + + /** + * Read text in RAW mode. + * + * @param string $tok The current token. + * + * @return bool + */ + protected function rawText($tok) + { + if (is_null($this->untilTag)) { + return $this->text($tok); + } + + $sequence = 'untilTag . '>'; + $txt = $this->readUntilSequence($sequence); + $this->events->text($txt); + $this->setTextMode(0); + + return $this->endTag(); + } + + /** + * Read text in RCDATA mode. + * + * @param string $tok The current token. + * + * @return bool + */ + protected function rcdata($tok) + { + if (is_null($this->untilTag)) { + return $this->text($tok); + } + + $sequence = 'untilTag; + $txt = ''; + + $caseSensitive = !Elements::isHtml5Element($this->untilTag); + while (false !== $tok && !('<' == $tok && ($this->scanner->sequenceMatches($sequence, $caseSensitive)))) { + if ('&' == $tok) { + $txt .= $this->decodeCharacterReference(); + $tok = $this->scanner->current(); + } else { + $txt .= $tok; + $tok = $this->scanner->next(); + } + } + $len = strlen($sequence); + $this->scanner->consume($len); + $len += $this->scanner->whitespace(); + if ('>' !== $this->scanner->current()) { + $this->parseError('Unclosed RCDATA end tag'); + } + + $this->scanner->unconsume($len); + $this->events->text($txt); + $this->setTextMode(0); + + return $this->endTag(); + } + + /** + * If the document is read, emit an EOF event. + */ + protected function eof() + { + // fprintf(STDOUT, "EOF"); + $this->flushBuffer(); + $this->events->eof(); + $this->carryOn = false; + } + + /** + * Look for markup. + */ + protected function markupDeclaration() + { + $tok = $this->scanner->next(); + + // Comment: + if ('-' == $tok && '-' == $this->scanner->peek()) { + $this->scanner->consume(2); + + return $this->comment(); + } elseif ('D' == $tok || 'd' == $tok) { // Doctype + return $this->doctype(); + } elseif ('[' == $tok) { // CDATA section + return $this->cdataSection(); + } + + // FINISH + $this->parseError('Expected . Emit an empty comment because 8.2.4.46 says to. + if ('>' == $tok) { + // Parse error. Emit the comment token. + $this->parseError("Expected comment data, got '>'"); + $this->events->comment(''); + $this->scanner->consume(); + + return true; + } + + // Replace NULL with the replacement char. + if ("\0" == $tok) { + $tok = UTF8Utils::FFFD; + } + while (!$this->isCommentEnd()) { + $comment .= $tok; + $tok = $this->scanner->next(); + } + + $this->events->comment($comment); + $this->scanner->consume(); + + return true; + } + + /** + * Check if the scanner has reached the end of a comment. + * + * @return bool + */ + protected function isCommentEnd() + { + $tok = $this->scanner->current(); + + // EOF + if (false === $tok) { + // Hit the end. + $this->parseError('Unexpected EOF in a comment.'); + + return true; + } + + // If next two tokens are not '--', not the end. + if ('-' != $tok || '-' != $this->scanner->peek()) { + return false; + } + + $this->scanner->consume(2); // Consume '-' and one of '!' or '>' + + // Test for '>' + if ('>' == $this->scanner->current()) { + return true; + } + // Test for '!>' + if ('!' == $this->scanner->current() && '>' == $this->scanner->peek()) { + $this->scanner->consume(); // Consume the last '>' + return true; + } + // Unread '-' and one of '!' or '>'; + $this->scanner->unconsume(2); + + return false; + } + + /** + * Parse a DOCTYPE. + * + * Parse a DOCTYPE declaration. This method has strong bearing on whether or + * not Quirksmode is enabled on the event handler. + * + * @todo This method is a little long. Should probably refactor. + * + * @return bool + */ + protected function doctype() + { + // Check that string is DOCTYPE. + if ($this->scanner->sequenceMatches('DOCTYPE', false)) { + $this->scanner->consume(7); + } else { + $chars = $this->scanner->charsWhile('DOCTYPEdoctype'); + $this->parseError('Expected DOCTYPE, got %s', $chars); + + return $this->bogusComment('scanner->whitespace(); + $tok = $this->scanner->current(); + + // EOF: die. + if (false === $tok) { + $this->events->doctype('html5', EventHandler::DOCTYPE_NONE, '', true); + $this->eof(); + + return true; + } + + // NULL char: convert. + if ("\0" === $tok) { + $this->parseError('Unexpected null character in DOCTYPE.'); + } + + $stop = " \n\f>"; + $doctypeName = $this->scanner->charsUntil($stop); + // Lowercase ASCII, replace \0 with FFFD + $doctypeName = strtolower(strtr($doctypeName, "\0", UTF8Utils::FFFD)); + + $tok = $this->scanner->current(); + + // If false, emit a parse error, DOCTYPE, and return. + if (false === $tok) { + $this->parseError('Unexpected EOF in DOCTYPE declaration.'); + $this->events->doctype($doctypeName, EventHandler::DOCTYPE_NONE, null, true); + + return true; + } + + // Short DOCTYPE, like + if ('>' == $tok) { + // DOCTYPE without a name. + if (0 == strlen($doctypeName)) { + $this->parseError('Expected a DOCTYPE name. Got nothing.'); + $this->events->doctype($doctypeName, 0, null, true); + $this->scanner->consume(); + + return true; + } + $this->events->doctype($doctypeName); + $this->scanner->consume(); + + return true; + } + $this->scanner->whitespace(); + + $pub = strtoupper($this->scanner->getAsciiAlpha()); + $white = $this->scanner->whitespace(); + + // Get ID, and flag it as pub or system. + if (('PUBLIC' == $pub || 'SYSTEM' == $pub) && $white > 0) { + // Get the sys ID. + $type = 'PUBLIC' == $pub ? EventHandler::DOCTYPE_PUBLIC : EventHandler::DOCTYPE_SYSTEM; + $id = $this->quotedString("\0>"); + if (false === $id) { + $this->events->doctype($doctypeName, $type, $pub, false); + + return true; + } + + // Premature EOF. + if (false === $this->scanner->current()) { + $this->parseError('Unexpected EOF in DOCTYPE'); + $this->events->doctype($doctypeName, $type, $id, true); + + return true; + } + + // Well-formed complete DOCTYPE. + $this->scanner->whitespace(); + if ('>' == $this->scanner->current()) { + $this->events->doctype($doctypeName, $type, $id, false); + $this->scanner->consume(); + + return true; + } + + // If we get here, we have scanner->charsUntil('>'); + $this->parseError('Malformed DOCTYPE.'); + $this->events->doctype($doctypeName, $type, $id, true); + $this->scanner->consume(); + + return true; + } + + // Else it's a bogus DOCTYPE. + // Consume to > and trash. + $this->scanner->charsUntil('>'); + + $this->parseError('Expected PUBLIC or SYSTEM. Got %s.', $pub); + $this->events->doctype($doctypeName, 0, null, true); + $this->scanner->consume(); + + return true; + } + + /** + * Utility for reading a quoted string. + * + * @param string $stopchars Characters (in addition to a close-quote) that should stop the string. + * E.g. sometimes '>' is higher precedence than '"' or "'". + * + * @return mixed String if one is found (quotations omitted). + */ + protected function quotedString($stopchars) + { + $tok = $this->scanner->current(); + if ('"' == $tok || "'" == $tok) { + $this->scanner->consume(); + $ret = $this->scanner->charsUntil($tok . $stopchars); + if ($this->scanner->current() == $tok) { + $this->scanner->consume(); + } else { + // Parse error because no close quote. + $this->parseError('Expected %s, got %s', $tok, $this->scanner->current()); + } + + return $ret; + } + + return false; + } + + /** + * Handle a CDATA section. + * + * @return bool + */ + protected function cdataSection() + { + $cdata = ''; + $this->scanner->consume(); + + $chars = $this->scanner->charsWhile('CDAT'); + if ('CDATA' != $chars || '[' != $this->scanner->current()) { + $this->parseError('Expected [CDATA[, got %s', $chars); + + return $this->bogusComment('scanner->next(); + do { + if (false === $tok) { + $this->parseError('Unexpected EOF inside CDATA.'); + $this->bogusComment('scanner->next(); + } while (!$this->scanner->sequenceMatches(']]>')); + + // Consume ]]> + $this->scanner->consume(3); + + $this->events->cdata($cdata); + + return true; + } + + // ================================================================ + // Non-HTML5 + // ================================================================ + + /** + * Handle a processing instruction. + * + * XML processing instructions are supposed to be ignored in HTML5, + * treated as "bogus comments". However, since we're not a user + * agent, we allow them. We consume until ?> and then issue a + * EventListener::processingInstruction() event. + * + * @return bool + */ + protected function processingInstruction() + { + if ('?' != $this->scanner->current()) { + return false; + } + + $tok = $this->scanner->next(); + $procName = $this->scanner->getAsciiAlpha(); + $white = $this->scanner->whitespace(); + + // If not a PI, send to bogusComment. + if (0 == strlen($procName) || 0 == $white || false == $this->scanner->current()) { + $this->parseError("Expected processing instruction name, got $tok"); + $this->bogusComment('. + while (!('?' == $this->scanner->current() && '>' == $this->scanner->peek())) { + $data .= $this->scanner->current(); + + $tok = $this->scanner->next(); + if (false === $tok) { + $this->parseError('Unexpected EOF in processing instruction.'); + $this->events->processingInstruction($procName, $data); + + return true; + } + } + + $this->scanner->consume(2); // Consume the closing tag + $this->events->processingInstruction($procName, $data); + + return true; + } + + // ================================================================ + // UTILITY FUNCTIONS + // ================================================================ + + /** + * Read from the input stream until we get to the desired sequene + * or hit the end of the input stream. + * + * @param string $sequence + * + * @return string + */ + protected function readUntilSequence($sequence) + { + $buffer = ''; + + // Optimization for reading larger blocks faster. + $first = substr($sequence, 0, 1); + while (false !== $this->scanner->current()) { + $buffer .= $this->scanner->charsUntil($first); + + // Stop as soon as we hit the stopping condition. + if ($this->scanner->sequenceMatches($sequence, false)) { + return $buffer; + } + $buffer .= $this->scanner->current(); + $this->scanner->consume(); + } + + // If we get here, we hit the EOF. + $this->parseError('Unexpected EOF during text read.'); + + return $buffer; + } + + /** + * Check if upcomming chars match the given sequence. + * + * This will read the stream for the $sequence. If it's + * found, this will return true. If not, return false. + * Since this unconsumes any chars it reads, the caller + * will still need to read the next sequence, even if + * this returns true. + * + * Example: $this->scanner->sequenceMatches('') will + * see if the input stream is at the start of a + * '' string. + * + * @param string $sequence + * @param bool $caseSensitive + * + * @return bool + */ + protected function sequenceMatches($sequence, $caseSensitive = true) + { + @trigger_error(__METHOD__ . ' method is deprecated since version 2.4 and will be removed in 3.0. Use Scanner::sequenceMatches() instead.', E_USER_DEPRECATED); + + return $this->scanner->sequenceMatches($sequence, $caseSensitive); + } + + /** + * Send a TEXT event with the contents of the text buffer. + * + * This emits an EventHandler::text() event with the current contents of the + * temporary text buffer. (The buffer is used to group as much PCDATA + * as we can instead of emitting lots and lots of TEXT events.) + */ + protected function flushBuffer() + { + if ('' === $this->text) { + return; + } + $this->events->text($this->text); + $this->text = ''; + } + + /** + * Add text to the temporary buffer. + * + * @see flushBuffer() + * + * @param string $str + */ + protected function buffer($str) + { + $this->text .= $str; + } + + /** + * Emit a parse error. + * + * A parse error always returns false because it never consumes any + * characters. + * + * @param string $msg + * + * @return string + */ + protected function parseError($msg) + { + $args = func_get_args(); + + if (count($args) > 1) { + array_shift($args); + $msg = vsprintf($msg, $args); + } + + $line = $this->scanner->currentLine(); + $col = $this->scanner->columnOffset(); + $this->events->parseError($msg, $line, $col); + + return false; + } + + /** + * Decode a character reference and return the string. + * + * If $inAttribute is set to true, a bare & will be returned as-is. + * + * @param bool $inAttribute Set to true if the text is inside of an attribute value. + * false otherwise. + * + * @return string + */ + protected function decodeCharacterReference($inAttribute = false) + { + // Next char after &. + $tok = $this->scanner->next(); + $start = $this->scanner->position(); + + if (false === $tok) { + return '&'; + } + + // These indicate not an entity. We return just + // the &. + if ("\t" === $tok || "\n" === $tok || "\f" === $tok || ' ' === $tok || '&' === $tok || '<' === $tok) { + // $this->scanner->next(); + return '&'; + } + + // Numeric entity + if ('#' === $tok) { + $tok = $this->scanner->next(); + + if (false === $tok) { + $this->parseError('Expected &#DEC; &#HEX;, got EOF'); + $this->scanner->unconsume(1); + + return '&'; + } + + // Hexidecimal encoding. + // X[0-9a-fA-F]+; + // x[0-9a-fA-F]+; + if ('x' === $tok || 'X' === $tok) { + $tok = $this->scanner->next(); // Consume x + + // Convert from hex code to char. + $hex = $this->scanner->getHex(); + if (empty($hex)) { + $this->parseError('Expected &#xHEX;, got &#x%s', $tok); + // We unconsume because we don't know what parser rules might + // be in effect for the remaining chars. For example. '&#>' + // might result in a specific parsing rule inside of tag + // contexts, while not inside of pcdata context. + $this->scanner->unconsume(2); + + return '&'; + } + $entity = CharacterReference::lookupHex($hex); + } // Decimal encoding. + // [0-9]+; + else { + // Convert from decimal to char. + $numeric = $this->scanner->getNumeric(); + if (false === $numeric) { + $this->parseError('Expected &#DIGITS;, got &#%s', $tok); + $this->scanner->unconsume(2); + + return '&'; + } + $entity = CharacterReference::lookupDecimal($numeric); + } + } elseif ('=' === $tok && $inAttribute) { + return '&'; + } else { // String entity. + // Attempt to consume a string up to a ';'. + // [a-zA-Z0-9]+; + $cname = $this->scanner->getAsciiAlphaNum(); + $entity = CharacterReference::lookupName($cname); + + // When no entity is found provide the name of the unmatched string + // and continue on as the & is not part of an entity. The & will + // be converted to & elsewhere. + if (null === $entity) { + if (!$inAttribute || '' === $cname) { + $this->parseError("No match in entity table for '%s'", $cname); + } + $this->scanner->unconsume($this->scanner->position() - $start); + + return '&'; + } + } + + // The scanner has advanced the cursor for us. + $tok = $this->scanner->current(); + + // We have an entity. We're done here. + if (';' === $tok) { + $this->scanner->consume(); + + return $entity; + } + + // Failing to match ; means unconsume the entire string. + $this->scanner->unconsume($this->scanner->position() - $start); + + $this->parseError('Expected &ENTITY;, got &ENTITY%s (no trailing ;) ', $tok); + + return '&'; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php new file mode 100644 index 000000000..00d3951fd --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php @@ -0,0 +1,127 @@ + 1, + 'dd' => 1, + 'dt' => 1, + 'rt' => 1, + 'rp' => 1, + 'tr' => 1, + 'th' => 1, + 'td' => 1, + 'thead' => 1, + 'tfoot' => 1, + 'tbody' => 1, + 'table' => 1, + 'optgroup' => 1, + 'option' => 1, + ); + + /** + * Returns true if the given tagname has special processing rules. + */ + public function hasRules($tagname) + { + return isset(static::$tags[$tagname]); + } + + /** + * Evaluate the rule for the current tag name. + * + * This may modify the existing DOM. + * + * @return \DOMElement The new Current DOM element. + */ + public function evaluate($new, $current) + { + switch ($new->tagName) { + case 'li': + return $this->handleLI($new, $current); + case 'dt': + case 'dd': + return $this->handleDT($new, $current); + case 'rt': + case 'rp': + return $this->handleRT($new, $current); + case 'optgroup': + return $this->closeIfCurrentMatches($new, $current, array( + 'optgroup', + )); + case 'option': + return $this->closeIfCurrentMatches($new, $current, array( + 'option', + )); + case 'tr': + return $this->closeIfCurrentMatches($new, $current, array( + 'tr', + )); + case 'td': + case 'th': + return $this->closeIfCurrentMatches($new, $current, array( + 'th', + 'td', + )); + case 'tbody': + case 'thead': + case 'tfoot': + case 'table': // Spec isn't explicit about this, but it's necessary. + + return $this->closeIfCurrentMatches($new, $current, array( + 'thead', + 'tfoot', + 'tbody', + )); + } + + return $current; + } + + protected function handleLI($ele, $current) + { + return $this->closeIfCurrentMatches($ele, $current, array( + 'li', + )); + } + + protected function handleDT($ele, $current) + { + return $this->closeIfCurrentMatches($ele, $current, array( + 'dt', + 'dd', + )); + } + + protected function handleRT($ele, $current) + { + return $this->closeIfCurrentMatches($ele, $current, array( + 'rt', + 'rp', + )); + } + + protected function closeIfCurrentMatches($ele, $current, $match) + { + if (in_array($current->tagName, $match, true)) { + $current->parentNode->appendChild($ele); + } else { + $current->appendChild($ele); + } + + return $ele; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/UTF8Utils.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/UTF8Utils.php new file mode 100644 index 000000000..f6a70bfac --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Parser/UTF8Utils.php @@ -0,0 +1,183 @@ + + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +use Masterminds\HTML5\Exception; + +class UTF8Utils +{ + /** + * The Unicode replacement character. + */ + const FFFD = "\xEF\xBF\xBD"; + + /** + * Count the number of characters in a string. + * UTF-8 aware. This will try (in order) iconv, MB, libxml, and finally a custom counter. + * + * @param string $string + * + * @return int + */ + public static function countChars($string) + { + // Get the length for the string we need. + if (function_exists('mb_strlen')) { + return mb_strlen($string, 'utf-8'); + } + + if (function_exists('iconv_strlen')) { + return iconv_strlen($string, 'utf-8'); + } + + if (function_exists('utf8_decode')) { + // MPB: Will this work? Won't certain decodes lead to two chars + // extrapolated out of 2-byte chars? + return strlen(utf8_decode($string)); + } + + $count = count_chars($string); + + // 0x80 = 0x7F - 0 + 1 (one added to get inclusive range) + // 0x33 = 0xF4 - 0x2C + 1 (one added to get inclusive range) + return array_sum(array_slice($count, 0, 0x80)) + array_sum(array_slice($count, 0xC2, 0x33)); + } + + /** + * Convert data from the given encoding to UTF-8. + * + * This has not yet been tested with charactersets other than UTF-8. + * It should work with ISO-8859-1/-13 and standard Latin Win charsets. + * + * @param string $data The data to convert + * @param string $encoding A valid encoding. Examples: http://www.php.net/manual/en/mbstring.supported-encodings.php + * + * @return string + */ + public static function convertToUTF8($data, $encoding = 'UTF-8') + { + /* + * From the HTML5 spec: Given an encoding, the bytes in the input stream must be converted + * to Unicode characters for the tokeniser, as described by the rules for that encoding, + * except that the leading U+FEFF BYTE ORDER MARK character, if any, must not be stripped + * by the encoding layer (it is stripped by the rule below). Bytes or sequences of bytes + * in the original byte stream that could not be converted to Unicode characters must be + * converted to U+FFFD REPLACEMENT CHARACTER code points. + */ + + // mb_convert_encoding is chosen over iconv because of a bug. The best + // details for the bug are on http://us1.php.net/manual/en/function.iconv.php#108643 + // which contains links to the actual but reports as well as work around + // details. + if (function_exists('mb_convert_encoding')) { + // mb library has the following behaviors: + // - UTF-16 surrogates result in false. + // - Overlongs and outside Plane 16 result in empty strings. + + // Before we run mb_convert_encoding we need to tell it what to do with + // characters it does not know. This could be different than the parent + // application executing this library so we store the value, change it + // to our needs, and then change it back when we are done. This feels + // a little excessive and it would be great if there was a better way. + $save = mb_substitute_character(); + mb_substitute_character('none'); + $data = mb_convert_encoding($data, 'UTF-8', $encoding); + mb_substitute_character($save); + } + // @todo Get iconv running in at least some environments if that is possible. + elseif (function_exists('iconv') && 'auto' !== $encoding) { + // fprintf(STDOUT, "iconv found\n"); + // iconv has the following behaviors: + // - Overlong representations are ignored. + // - Beyond Plane 16 is replaced with a lower char. + // - Incomplete sequences generate a warning. + $data = @iconv($encoding, 'UTF-8//IGNORE', $data); + } else { + throw new Exception('Not implemented, please install mbstring or iconv'); + } + + /* + * One leading U+FEFF BYTE ORDER MARK character must be ignored if any are present. + */ + if ("\xEF\xBB\xBF" === substr($data, 0, 3)) { + $data = substr($data, 3); + } + + return $data; + } + + /** + * Checks for Unicode code points that are not valid in a document. + * + * @param string $data A string to analyze + * + * @return array An array of (string) error messages produced by the scanning + */ + public static function checkForIllegalCodepoints($data) + { + // Vestigal error handling. + $errors = array(); + + /* + * All U+0000 null characters in the input must be replaced by U+FFFD REPLACEMENT CHARACTERs. + * Any occurrences of such characters is a parse error. + */ + for ($i = 0, $count = substr_count($data, "\0"); $i < $count; ++$i) { + $errors[] = 'null-character'; + } + + /* + * Any occurrences of any characters in the ranges U+0001 to U+0008, U+000B, U+000E to U+001F, U+007F + * to U+009F, U+D800 to U+DFFF , U+FDD0 to U+FDEF, and characters U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, + * U+2FFFE, U+2FFFF, U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE, U+6FFFF, U+7FFFE, + * U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, + * U+DFFFE, U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, and U+10FFFF are parse errors. + * (These are all control characters or permanently undefined Unicode characters.) + */ + // Check PCRE is loaded. + $count = preg_match_all( + '/(?: + [\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F + | + \xC2[\x80-\x9F] # U+0080 to U+009F + | + \xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF + | + \xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF + | + \xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF + | + [\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16}) + )/x', $data, $matches); + for ($i = 0; $i < $count; ++$i) { + $errors[] = 'invalid-codepoint'; + } + + return $errors; + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php new file mode 100644 index 000000000..e9421a12d --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php @@ -0,0 +1,1533 @@ + ' ', + "\n" => ' ', + '!' => '!', + '"' => '"', + '#' => '#', + '$' => '$', + '%' => '%', + '&' => '&', + '\'' => ''', + '(' => '(', + ')' => ')', + '*' => '*', + '+' => '+', + ',' => ',', + '.' => '.', + '/' => '/', + ':' => ':', + ';' => ';', + '<' => '<', + '<⃒' => '&nvlt', + '=' => '=', + '=⃥' => '&bne', + '>' => '>', + '>⃒' => '&nvgt', + '?' => '?', + '@' => '@', + '[' => '[', + '\\' => '\', + ']' => ']', + '^' => '^', + '_' => '_', + '`' => '`', + 'fj' => '&fjlig', + '{' => '{', + '|' => '|', + '}' => '}', + ' ' => ' ', + '¡' => '¡', + '¢' => '¢', + '£' => '£', + '¤' => '¤', + '¥' => '¥', + '¦' => '¦', + '§' => '§', + '¨' => '¨', + '©' => '©', + 'ª' => 'ª', + '«' => '«', + '¬' => '¬', + '­' => '­', + '®' => '®', + '¯' => '¯', + '°' => '°', + '±' => '±', + '²' => '²', + '³' => '³', + '´' => '´', + 'µ' => 'µ', + '¶' => '¶', + '·' => '·', + '¸' => '¸', + '¹' => '¹', + 'º' => 'º', + '»' => '»', + '¼' => '¼', + '½' => '½', + '¾' => '¾', + '¿' => '¿', + 'À' => 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Æ' => 'Æ', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ð' => 'Ð', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + '×' => '×', + 'Ø' => 'Ø', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'Þ' => 'Þ', + 'ß' => 'ß', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'æ' => 'æ', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ð' => 'ð', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + '÷' => '÷', + 'ø' => 'ø', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'þ' => 'þ', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Đ' => 'Đ', + 'đ' => 'đ', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ħ' => 'Ħ', + 'ħ' => 'ħ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'ı' => 'ı', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'ĸ' => 'ĸ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ŀ' => 'Ŀ', + 'ŀ' => 'ŀ', + 'Ł' => 'Ł', + 'ł' => 'ł', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'ʼn' => 'ʼn', + 'Ŋ' => 'Ŋ', + 'ŋ' => 'ŋ', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Œ' => 'Œ', + 'œ' => 'œ', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ŧ' => 'Ŧ', + 'ŧ' => 'ŧ', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'ƒ' => 'ƒ', + 'Ƶ' => 'Ƶ', + 'ǵ' => 'ǵ', + 'ȷ' => 'ȷ', + 'ˆ' => 'ˆ', + 'ˇ' => 'ˇ', + '˘' => '˘', + '˙' => '˙', + '˚' => '˚', + '˛' => '˛', + '˜' => '˜', + '˝' => '˝', + '̑' => '̑', + 'Α' => 'Α', + 'Β' => 'Β', + 'Γ' => 'Γ', + 'Δ' => 'Δ', + 'Ε' => 'Ε', + 'Ζ' => 'Ζ', + 'Η' => 'Η', + 'Θ' => 'Θ', + 'Ι' => 'Ι', + 'Κ' => 'Κ', + 'Λ' => 'Λ', + 'Μ' => 'Μ', + 'Ν' => 'Ν', + 'Ξ' => 'Ξ', + 'Ο' => 'Ο', + 'Π' => 'Π', + 'Ρ' => 'Ρ', + 'Σ' => 'Σ', + 'Τ' => 'Τ', + 'Υ' => 'Υ', + 'Φ' => 'Φ', + 'Χ' => 'Χ', + 'Ψ' => 'Ψ', + 'Ω' => 'Ω', + 'α' => 'α', + 'β' => 'β', + 'γ' => 'γ', + 'δ' => 'δ', + 'ε' => 'ε', + 'ζ' => 'ζ', + 'η' => 'η', + 'θ' => 'θ', + 'ι' => 'ι', + 'κ' => 'κ', + 'λ' => 'λ', + 'μ' => 'μ', + 'ν' => 'ν', + 'ξ' => 'ξ', + 'ο' => 'ο', + 'π' => 'π', + 'ρ' => 'ρ', + 'ς' => 'ς', + 'σ' => 'σ', + 'τ' => 'τ', + 'υ' => 'υ', + 'φ' => 'φ', + 'χ' => 'χ', + 'ψ' => 'ψ', + 'ω' => 'ω', + 'ϑ' => 'ϑ', + 'ϒ' => 'ϒ', + 'ϕ' => 'ϕ', + 'ϖ' => 'ϖ', + 'Ϝ' => 'Ϝ', + 'ϝ' => 'ϝ', + 'ϰ' => 'ϰ', + 'ϱ' => 'ϱ', + 'ϵ' => 'ϵ', + '϶' => '϶', + 'Ё' => 'Ё', + 'Ђ' => 'Ђ', + 'Ѓ' => 'Ѓ', + 'Є' => 'Є', + 'Ѕ' => 'Ѕ', + 'І' => 'І', + 'Ї' => 'Ї', + 'Ј' => 'Ј', + 'Љ' => 'Љ', + 'Њ' => 'Њ', + 'Ћ' => 'Ћ', + 'Ќ' => 'Ќ', + 'Ў' => 'Ў', + 'Џ' => 'Џ', + 'А' => 'А', + 'Б' => 'Б', + 'В' => 'В', + 'Г' => 'Г', + 'Д' => 'Д', + 'Е' => 'Е', + 'Ж' => 'Ж', + 'З' => 'З', + 'И' => 'И', + 'Й' => 'Й', + 'К' => 'К', + 'Л' => 'Л', + 'М' => 'М', + 'Н' => 'Н', + 'О' => 'О', + 'П' => 'П', + 'Р' => 'Р', + 'С' => 'С', + 'Т' => 'Т', + 'У' => 'У', + 'Ф' => 'Ф', + 'Х' => 'Х', + 'Ц' => 'Ц', + 'Ч' => 'Ч', + 'Ш' => 'Ш', + 'Щ' => 'Щ', + 'Ъ' => 'Ъ', + 'Ы' => 'Ы', + 'Ь' => 'Ь', + 'Э' => 'Э', + 'Ю' => 'Ю', + 'Я' => 'Я', + 'а' => 'а', + 'б' => 'б', + 'в' => 'в', + 'г' => 'г', + 'д' => 'д', + 'е' => 'е', + 'ж' => 'ж', + 'з' => 'з', + 'и' => 'и', + 'й' => 'й', + 'к' => 'к', + 'л' => 'л', + 'м' => 'м', + 'н' => 'н', + 'о' => 'о', + 'п' => 'п', + 'р' => 'р', + 'с' => 'с', + 'т' => 'т', + 'у' => 'у', + 'ф' => 'ф', + 'х' => 'х', + 'ц' => 'ц', + 'ч' => 'ч', + 'ш' => 'ш', + 'щ' => 'щ', + 'ъ' => 'ъ', + 'ы' => 'ы', + 'ь' => 'ь', + 'э' => 'э', + 'ю' => 'ю', + 'я' => 'я', + 'ё' => 'ё', + 'ђ' => 'ђ', + 'ѓ' => 'ѓ', + 'є' => 'є', + 'ѕ' => 'ѕ', + 'і' => 'і', + 'ї' => 'ї', + 'ј' => 'ј', + 'љ' => 'љ', + 'њ' => 'њ', + 'ћ' => 'ћ', + 'ќ' => 'ќ', + 'ў' => 'ў', + 'џ' => 'џ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + '​' => '​', + '‌' => '‌', + '‍' => '‍', + '‎' => '‎', + '‏' => '‏', + '‐' => '‐', + '–' => '–', + '—' => '—', + '―' => '―', + '‖' => '‖', + '‘' => '‘', + '’' => '’', + '‚' => '‚', + '“' => '“', + '”' => '”', + '„' => '„', + '†' => '†', + '‡' => '‡', + '•' => '•', + '‥' => '‥', + '…' => '…', + '‰' => '‰', + '‱' => '‱', + '′' => '′', + '″' => '″', + '‴' => '‴', + '‵' => '‵', + '‹' => '‹', + '›' => '›', + '‾' => '‾', + '⁁' => '⁁', + '⁃' => '⁃', + '⁄' => '⁄', + '⁏' => '⁏', + '⁗' => '⁗', + ' ' => ' ', + '  ' => '&ThickSpace', + '⁠' => '⁠', + '⁡' => '⁡', + '⁢' => '⁢', + '⁣' => '⁣', + '€' => '€', + '⃛' => '⃛', + '⃜' => '⃜', + 'ℂ' => 'ℂ', + '℅' => '℅', + 'ℊ' => 'ℊ', + 'ℋ' => 'ℋ', + 'ℌ' => 'ℌ', + 'ℍ' => 'ℍ', + 'ℎ' => 'ℎ', + 'ℏ' => 'ℏ', + 'ℐ' => 'ℐ', + 'ℑ' => 'ℑ', + 'ℒ' => 'ℒ', + 'ℓ' => 'ℓ', + 'ℕ' => 'ℕ', + '№' => '№', + '℗' => '℗', + '℘' => '℘', + 'ℙ' => 'ℙ', + 'ℚ' => 'ℚ', + 'ℛ' => 'ℛ', + 'ℜ' => 'ℜ', + 'ℝ' => 'ℝ', + '℞' => '℞', + '™' => '™', + 'ℤ' => 'ℤ', + '℧' => '℧', + 'ℨ' => 'ℨ', + '℩' => '℩', + 'ℬ' => 'ℬ', + 'ℭ' => 'ℭ', + 'ℯ' => 'ℯ', + 'ℰ' => 'ℰ', + 'ℱ' => 'ℱ', + 'ℳ' => 'ℳ', + 'ℴ' => 'ℴ', + 'ℵ' => 'ℵ', + 'ℶ' => 'ℶ', + 'ℷ' => 'ℷ', + 'ℸ' => 'ℸ', + 'ⅅ' => 'ⅅ', + 'ⅆ' => 'ⅆ', + 'ⅇ' => 'ⅇ', + 'ⅈ' => 'ⅈ', + '⅓' => '⅓', + '⅔' => '⅔', + '⅕' => '⅕', + '⅖' => '⅖', + '⅗' => '⅗', + '⅘' => '⅘', + '⅙' => '⅙', + '⅚' => '⅚', + '⅛' => '⅛', + '⅜' => '⅜', + '⅝' => '⅝', + '⅞' => '⅞', + '←' => '←', + '↑' => '↑', + '→' => '→', + '↓' => '↓', + '↔' => '↔', + '↕' => '↕', + '↖' => '↖', + '↗' => '↗', + '↘' => '↘', + '↙' => '↙', + '↚' => '↚', + '↛' => '↛', + '↝' => '↝', + '↝̸' => '&nrarrw', + '↞' => '↞', + '↟' => '↟', + '↠' => '↠', + '↡' => '↡', + '↢' => '↢', + '↣' => '↣', + '↤' => '↤', + '↥' => '↥', + '↦' => '↦', + '↧' => '↧', + '↩' => '↩', + '↪' => '↪', + '↫' => '↫', + '↬' => '↬', + '↭' => '↭', + '↮' => '↮', + '↰' => '↰', + '↱' => '↱', + '↲' => '↲', + '↳' => '↳', + '↵' => '↵', + '↶' => '↶', + '↷' => '↷', + '↺' => '↺', + '↻' => '↻', + '↼' => '↼', + '↽' => '↽', + '↾' => '↾', + '↿' => '↿', + '⇀' => '⇀', + '⇁' => '⇁', + '⇂' => '⇂', + '⇃' => '⇃', + '⇄' => '⇄', + '⇅' => '⇅', + '⇆' => '⇆', + '⇇' => '⇇', + '⇈' => '⇈', + '⇉' => '⇉', + '⇊' => '⇊', + '⇋' => '⇋', + '⇌' => '⇌', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '⇐' => '⇐', + '⇑' => '⇑', + '⇒' => '⇒', + '⇓' => '⇓', + '⇔' => '⇔', + '⇕' => '⇕', + '⇖' => '⇖', + '⇗' => '⇗', + '⇘' => '⇘', + '⇙' => '⇙', + '⇚' => '⇚', + '⇛' => '⇛', + '⇝' => '⇝', + '⇤' => '⇤', + '⇥' => '⇥', + '⇵' => '⇵', + '⇽' => '⇽', + '⇾' => '⇾', + '⇿' => '⇿', + '∀' => '∀', + '∁' => '∁', + '∂' => '∂', + '∂̸' => '&npart', + '∃' => '∃', + '∄' => '∄', + '∅' => '∅', + '∇' => '∇', + '∈' => '∈', + '∉' => '∉', + '∋' => '∋', + '∌' => '∌', + '∏' => '∏', + '∐' => '∐', + '∑' => '∑', + '−' => '−', + '∓' => '∓', + '∔' => '∔', + '∖' => '∖', + '∗' => '∗', + '∘' => '∘', + '√' => '√', + '∝' => '∝', + '∞' => '∞', + '∟' => '∟', + '∠' => '∠', + '∠⃒' => '&nang', + '∡' => '∡', + '∢' => '∢', + '∣' => '∣', + '∤' => '∤', + '∥' => '∥', + '∦' => '∦', + '∧' => '∧', + '∨' => '∨', + '∩' => '∩', + '∩︀' => '&caps', + '∪' => '∪', + '∪︀' => '&cups', + '∫' => '∫', + '∬' => '∬', + '∭' => '∭', + '∮' => '∮', + '∯' => '∯', + '∰' => '∰', + '∱' => '∱', + '∲' => '∲', + '∳' => '∳', + '∴' => '∴', + '∵' => '∵', + '∶' => '∶', + '∷' => '∷', + '∸' => '∸', + '∺' => '∺', + '∻' => '∻', + '∼' => '∼', + '∼⃒' => '&nvsim', + '∽' => '∽', + '∽̱' => '&race', + '∾' => '∾', + '∾̳' => '&acE', + '∿' => '∿', + '≀' => '≀', + '≁' => '≁', + '≂' => '≂', + '≂̸' => '&nesim', + '≃' => '≃', + '≄' => '≄', + '≅' => '≅', + '≆' => '≆', + '≇' => '≇', + '≈' => '≈', + '≉' => '≉', + '≊' => '≊', + '≋' => '≋', + '≋̸' => '&napid', + '≌' => '≌', + '≍' => '≍', + '≍⃒' => '&nvap', + '≎' => '≎', + '≎̸' => '&nbump', + '≏' => '≏', + '≏̸' => '&nbumpe', + '≐' => '≐', + '≐̸' => '&nedot', + '≑' => '≑', + '≒' => '≒', + '≓' => '≓', + '≔' => '≔', + '≕' => '≕', + '≖' => '≖', + '≗' => '≗', + '≙' => '≙', + '≚' => '≚', + '≜' => '≜', + '≟' => '≟', + '≠' => '≠', + '≡' => '≡', + '≡⃥' => '&bnequiv', + '≢' => '≢', + '≤' => '≤', + '≤⃒' => '&nvle', + '≥' => '≥', + '≥⃒' => '&nvge', + '≦' => '≦', + '≦̸' => '&nlE', + '≧' => '≧', + '≧̸' => '&NotGreaterFullEqual', + '≨' => '≨', + '≨︀' => '&lvertneqq', + '≩' => '≩', + '≩︀' => '&gvertneqq', + '≪' => '≪', + '≪̸' => '&nLtv', + '≪⃒' => '&nLt', + '≫' => '≫', + '≫̸' => '&NotGreaterGreater', + '≫⃒' => '&nGt', + '≬' => '≬', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≲' => '≲', + '≳' => '≳', + '≴' => '≴', + '≵' => '≵', + '≶' => '≶', + '≷' => '≷', + '≸' => '≸', + '≹' => '≹', + '≺' => '≺', + '≻' => '≻', + '≼' => '≼', + '≽' => '≽', + '≾' => '≾', + '≿' => '≿', + '≿̸' => '&NotSucceedsTilde', + '⊀' => '⊀', + '⊁' => '⊁', + '⊂' => '⊂', + '⊂⃒' => '&vnsub', + '⊃' => '⊃', + '⊃⃒' => '&nsupset', + '⊄' => '⊄', + '⊅' => '⊅', + '⊆' => '⊆', + '⊇' => '⊇', + '⊈' => '⊈', + '⊉' => '⊉', + '⊊' => '⊊', + '⊊︀' => '&vsubne', + '⊋' => '⊋', + '⊋︀' => '&vsupne', + '⊍' => '⊍', + '⊎' => '⊎', + '⊏' => '⊏', + '⊏̸' => '&NotSquareSubset', + '⊐' => '⊐', + '⊐̸' => '&NotSquareSuperset', + '⊑' => '⊑', + '⊒' => '⊒', + '⊓' => '⊓', + '⊓︀' => '&sqcaps', + '⊔' => '⊔', + '⊔︀' => '&sqcups', + '⊕' => '⊕', + '⊖' => '⊖', + '⊗' => '⊗', + '⊘' => '⊘', + '⊙' => '⊙', + '⊚' => '⊚', + '⊛' => '⊛', + '⊝' => '⊝', + '⊞' => '⊞', + '⊟' => '⊟', + '⊠' => '⊠', + '⊡' => '⊡', + '⊢' => '⊢', + '⊣' => '⊣', + '⊤' => '⊤', + '⊥' => '⊥', + '⊧' => '⊧', + '⊨' => '⊨', + '⊩' => '⊩', + '⊪' => '⊪', + '⊫' => '⊫', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⊰' => '⊰', + '⊲' => '⊲', + '⊳' => '⊳', + '⊴' => '⊴', + '⊴⃒' => '&nvltrie', + '⊵' => '⊵', + '⊵⃒' => '&nvrtrie', + '⊶' => '⊶', + '⊷' => '⊷', + '⊸' => '⊸', + '⊹' => '⊹', + '⊺' => '⊺', + '⊻' => '⊻', + '⊽' => '⊽', + '⊾' => '⊾', + '⊿' => '⊿', + '⋀' => '⋀', + '⋁' => '⋁', + '⋂' => '⋂', + '⋃' => '⋃', + '⋄' => '⋄', + '⋅' => '⋅', + '⋆' => '⋆', + '⋇' => '⋇', + '⋈' => '⋈', + '⋉' => '⋉', + '⋊' => '⋊', + '⋋' => '⋋', + '⋌' => '⋌', + '⋍' => '⋍', + '⋎' => '⋎', + '⋏' => '⋏', + '⋐' => '⋐', + '⋑' => '⋑', + '⋒' => '⋒', + '⋓' => '⋓', + '⋔' => '⋔', + '⋕' => '⋕', + '⋖' => '⋖', + '⋗' => '⋗', + '⋘' => '⋘', + '⋘̸' => '&nLl', + '⋙' => '⋙', + '⋙̸' => '&nGg', + '⋚' => '⋚', + '⋚︀' => '&lesg', + '⋛' => '⋛', + '⋛︀' => '&gesl', + '⋞' => '⋞', + '⋟' => '⋟', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋦' => '⋦', + '⋧' => '⋧', + '⋨' => '⋨', + '⋩' => '⋩', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + '⋮' => '⋮', + '⋯' => '⋯', + '⋰' => '⋰', + '⋱' => '⋱', + '⋲' => '⋲', + '⋳' => '⋳', + '⋴' => '⋴', + '⋵' => '⋵', + '⋵̸' => '¬indot', + '⋶' => '⋶', + '⋷' => '⋷', + '⋹' => '⋹', + '⋹̸' => '¬inE', + '⋺' => '⋺', + '⋻' => '⋻', + '⋼' => '⋼', + '⋽' => '⋽', + '⋾' => '⋾', + '⌅' => '⌅', + '⌆' => '⌆', + '⌈' => '⌈', + '⌉' => '⌉', + '⌊' => '⌊', + '⌋' => '⌋', + '⌌' => '⌌', + '⌍' => '⌍', + '⌎' => '⌎', + '⌏' => '⌏', + '⌐' => '⌐', + '⌒' => '⌒', + '⌓' => '⌓', + '⌕' => '⌕', + '⌖' => '⌖', + '⌜' => '⌜', + '⌝' => '⌝', + '⌞' => '⌞', + '⌟' => '⌟', + '⌢' => '⌢', + '⌣' => '⌣', + '⌭' => '⌭', + '⌮' => '⌮', + '⌶' => '⌶', + '⌽' => '⌽', + '⌿' => '⌿', + '⍼' => '⍼', + '⎰' => '⎰', + '⎱' => '⎱', + '⎴' => '⎴', + '⎵' => '⎵', + '⎶' => '⎶', + '⏜' => '⏜', + '⏝' => '⏝', + '⏞' => '⏞', + '⏟' => '⏟', + '⏢' => '⏢', + '⏧' => '⏧', + '␣' => '␣', + 'Ⓢ' => 'Ⓢ', + '─' => '─', + '│' => '│', + '┌' => '┌', + '┐' => '┐', + '└' => '└', + '┘' => '┘', + '├' => '├', + '┤' => '┤', + '┬' => '┬', + '┴' => '┴', + '┼' => '┼', + '═' => '═', + '║' => '║', + '╒' => '╒', + '╓' => '╓', + '╔' => '╔', + '╕' => '╕', + '╖' => '╖', + '╗' => '╗', + '╘' => '╘', + '╙' => '╙', + '╚' => '╚', + '╛' => '╛', + '╜' => '╜', + '╝' => '╝', + '╞' => '╞', + '╟' => '╟', + '╠' => '╠', + '╡' => '╡', + '╢' => '╢', + '╣' => '╣', + '╤' => '╤', + '╥' => '╥', + '╦' => '╦', + '╧' => '╧', + '╨' => '╨', + '╩' => '╩', + '╪' => '╪', + '╫' => '╫', + '╬' => '╬', + '▀' => '▀', + '▄' => '▄', + '█' => '█', + '░' => '░', + '▒' => '▒', + '▓' => '▓', + '□' => '□', + '▪' => '▪', + '▫' => '▫', + '▭' => '▭', + '▮' => '▮', + '▱' => '▱', + '△' => '△', + '▴' => '▴', + '▵' => '▵', + '▸' => '▸', + '▹' => '▹', + '▽' => '▽', + '▾' => '▾', + '▿' => '▿', + '◂' => '◂', + '◃' => '◃', + '◊' => '◊', + '○' => '○', + '◬' => '◬', + '◯' => '◯', + '◸' => '◸', + '◹' => '◹', + '◺' => '◺', + '◻' => '◻', + '◼' => '◼', + '★' => '★', + '☆' => '☆', + '☎' => '☎', + '♀' => '♀', + '♂' => '♂', + '♠' => '♠', + '♣' => '♣', + '♥' => '♥', + '♦' => '♦', + '♪' => '♪', + '♭' => '♭', + '♮' => '♮', + '♯' => '♯', + '✓' => '✓', + '✗' => '✗', + '✠' => '✠', + '✶' => '✶', + '❘' => '❘', + '❲' => '❲', + '❳' => '❳', + '⟈' => '⟈', + '⟉' => '⟉', + '⟦' => '⟦', + '⟧' => '⟧', + '⟨' => '⟨', + '⟩' => '⟩', + '⟪' => '⟪', + '⟫' => '⟫', + '⟬' => '⟬', + '⟭' => '⟭', + '⟵' => '⟵', + '⟶' => '⟶', + '⟷' => '⟷', + '⟸' => '⟸', + '⟹' => '⟹', + '⟺' => '⟺', + '⟼' => '⟼', + '⟿' => '⟿', + '⤂' => '⤂', + '⤃' => '⤃', + '⤄' => '⤄', + '⤅' => '⤅', + '⤌' => '⤌', + '⤍' => '⤍', + '⤎' => '⤎', + '⤏' => '⤏', + '⤐' => '⤐', + '⤑' => '⤑', + '⤒' => '⤒', + '⤓' => '⤓', + '⤖' => '⤖', + '⤙' => '⤙', + '⤚' => '⤚', + '⤛' => '⤛', + '⤜' => '⤜', + '⤝' => '⤝', + '⤞' => '⤞', + '⤟' => '⤟', + '⤠' => '⤠', + '⤣' => '⤣', + '⤤' => '⤤', + '⤥' => '⤥', + '⤦' => '⤦', + '⤧' => '⤧', + '⤨' => '⤨', + '⤩' => '⤩', + '⤪' => '⤪', + '⤳' => '⤳', + '⤳̸' => '&nrarrc', + '⤵' => '⤵', + '⤶' => '⤶', + '⤷' => '⤷', + '⤸' => '⤸', + '⤹' => '⤹', + '⤼' => '⤼', + '⤽' => '⤽', + '⥅' => '⥅', + '⥈' => '⥈', + '⥉' => '⥉', + '⥊' => '⥊', + '⥋' => '⥋', + '⥎' => '⥎', + '⥏' => '⥏', + '⥐' => '⥐', + '⥑' => '⥑', + '⥒' => '⥒', + '⥓' => '⥓', + '⥔' => '⥔', + '⥕' => '⥕', + '⥖' => '⥖', + '⥗' => '⥗', + '⥘' => '⥘', + '⥙' => '⥙', + '⥚' => '⥚', + '⥛' => '⥛', + '⥜' => '⥜', + '⥝' => '⥝', + '⥞' => '⥞', + '⥟' => '⥟', + '⥠' => '⥠', + '⥡' => '⥡', + '⥢' => '⥢', + '⥣' => '⥣', + '⥤' => '⥤', + '⥥' => '⥥', + '⥦' => '⥦', + '⥧' => '⥧', + '⥨' => '⥨', + '⥩' => '⥩', + '⥪' => '⥪', + '⥫' => '⥫', + '⥬' => '⥬', + '⥭' => '⥭', + '⥮' => '⥮', + '⥯' => '⥯', + '⥰' => '⥰', + '⥱' => '⥱', + '⥲' => '⥲', + '⥳' => '⥳', + '⥴' => '⥴', + '⥵' => '⥵', + '⥶' => '⥶', + '⥸' => '⥸', + '⥹' => '⥹', + '⥻' => '⥻', + '⥼' => '⥼', + '⥽' => '⥽', + '⥾' => '⥾', + '⥿' => '⥿', + '⦅' => '⦅', + '⦆' => '⦆', + '⦋' => '⦋', + '⦌' => '⦌', + '⦍' => '⦍', + '⦎' => '⦎', + '⦏' => '⦏', + '⦐' => '⦐', + '⦑' => '⦑', + '⦒' => '⦒', + '⦓' => '⦓', + '⦔' => '⦔', + '⦕' => '⦕', + '⦖' => '⦖', + '⦚' => '⦚', + '⦜' => '⦜', + '⦝' => '⦝', + '⦤' => '⦤', + '⦥' => '⦥', + '⦦' => '⦦', + '⦧' => '⦧', + '⦨' => '⦨', + '⦩' => '⦩', + '⦪' => '⦪', + '⦫' => '⦫', + '⦬' => '⦬', + '⦭' => '⦭', + '⦮' => '⦮', + '⦯' => '⦯', + '⦰' => '⦰', + '⦱' => '⦱', + '⦲' => '⦲', + '⦳' => '⦳', + '⦴' => '⦴', + '⦵' => '⦵', + '⦶' => '⦶', + '⦷' => '⦷', + '⦹' => '⦹', + '⦻' => '⦻', + '⦼' => '⦼', + '⦾' => '⦾', + '⦿' => '⦿', + '⧀' => '⧀', + '⧁' => '⧁', + '⧂' => '⧂', + '⧃' => '⧃', + '⧄' => '⧄', + '⧅' => '⧅', + '⧉' => '⧉', + '⧍' => '⧍', + '⧎' => '⧎', + '⧏' => '⧏', + '⧏̸' => '&NotLeftTriangleBar', + '⧐' => '⧐', + '⧐̸' => '&NotRightTriangleBar', + '⧜' => '⧜', + '⧝' => '⧝', + '⧞' => '⧞', + '⧣' => '⧣', + '⧤' => '⧤', + '⧥' => '⧥', + '⧫' => '⧫', + '⧴' => '⧴', + '⧶' => '⧶', + '⨀' => '⨀', + '⨁' => '⨁', + '⨂' => '⨂', + '⨄' => '⨄', + '⨆' => '⨆', + '⨌' => '⨌', + '⨍' => '⨍', + '⨐' => '⨐', + '⨑' => '⨑', + '⨒' => '⨒', + '⨓' => '⨓', + '⨔' => '⨔', + '⨕' => '⨕', + '⨖' => '⨖', + '⨗' => '⨗', + '⨢' => '⨢', + '⨣' => '⨣', + '⨤' => '⨤', + '⨥' => '⨥', + '⨦' => '⨦', + '⨧' => '⨧', + '⨩' => '⨩', + '⨪' => '⨪', + '⨭' => '⨭', + '⨮' => '⨮', + '⨯' => '⨯', + '⨰' => '⨰', + '⨱' => '⨱', + '⨳' => '⨳', + '⨴' => '⨴', + '⨵' => '⨵', + '⨶' => '⨶', + '⨷' => '⨷', + '⨸' => '⨸', + '⨹' => '⨹', + '⨺' => '⨺', + '⨻' => '⨻', + '⨼' => '⨼', + '⨿' => '⨿', + '⩀' => '⩀', + '⩂' => '⩂', + '⩃' => '⩃', + '⩄' => '⩄', + '⩅' => '⩅', + '⩆' => '⩆', + '⩇' => '⩇', + '⩈' => '⩈', + '⩉' => '⩉', + '⩊' => '⩊', + '⩋' => '⩋', + '⩌' => '⩌', + '⩍' => '⩍', + '⩐' => '⩐', + '⩓' => '⩓', + '⩔' => '⩔', + '⩕' => '⩕', + '⩖' => '⩖', + '⩗' => '⩗', + '⩘' => '⩘', + '⩚' => '⩚', + '⩛' => '⩛', + '⩜' => '⩜', + '⩝' => '⩝', + '⩟' => '⩟', + '⩦' => '⩦', + '⩪' => '⩪', + '⩭' => '⩭', + '⩭̸' => '&ncongdot', + '⩮' => '⩮', + '⩯' => '⩯', + '⩰' => '⩰', + '⩰̸' => '&napE', + '⩱' => '⩱', + '⩲' => '⩲', + '⩳' => '⩳', + '⩴' => '⩴', + '⩵' => '⩵', + '⩷' => '⩷', + '⩸' => '⩸', + '⩹' => '⩹', + '⩺' => '⩺', + '⩻' => '⩻', + '⩼' => '⩼', + '⩽' => '⩽', + '⩽̸' => '&nles', + '⩾' => '⩾', + '⩾̸' => '&nges', + '⩿' => '⩿', + '⪀' => '⪀', + '⪁' => '⪁', + '⪂' => '⪂', + '⪃' => '⪃', + '⪄' => '⪄', + '⪅' => '⪅', + '⪆' => '⪆', + '⪇' => '⪇', + '⪈' => '⪈', + '⪉' => '⪉', + '⪊' => '⪊', + '⪋' => '⪋', + '⪌' => '⪌', + '⪍' => '⪍', + '⪎' => '⪎', + '⪏' => '⪏', + '⪐' => '⪐', + '⪑' => '⪑', + '⪒' => '⪒', + '⪓' => '⪓', + '⪔' => '⪔', + '⪕' => '⪕', + '⪖' => '⪖', + '⪗' => '⪗', + '⪘' => '⪘', + '⪙' => '⪙', + '⪚' => '⪚', + '⪝' => '⪝', + '⪞' => '⪞', + '⪟' => '⪟', + '⪠' => '⪠', + '⪡' => '⪡', + '⪡̸' => '&NotNestedLessLess', + '⪢' => '⪢', + '⪢̸' => '&NotNestedGreaterGreater', + '⪤' => '⪤', + '⪥' => '⪥', + '⪦' => '⪦', + '⪧' => '⪧', + '⪨' => '⪨', + '⪩' => '⪩', + '⪪' => '⪪', + '⪫' => '⪫', + '⪬' => '⪬', + '⪬︀' => '&smtes', + '⪭' => '⪭', + '⪭︀' => '&lates', + '⪮' => '⪮', + '⪯' => '⪯', + '⪯̸' => '&NotPrecedesEqual', + '⪰' => '⪰', + '⪰̸' => '&NotSucceedsEqual', + '⪳' => '⪳', + '⪴' => '⪴', + '⪵' => '⪵', + '⪶' => '⪶', + '⪷' => '⪷', + '⪸' => '⪸', + '⪹' => '⪹', + '⪺' => '⪺', + '⪻' => '⪻', + '⪼' => '⪼', + '⪽' => '⪽', + '⪾' => '⪾', + '⪿' => '⪿', + '⫀' => '⫀', + '⫁' => '⫁', + '⫂' => '⫂', + '⫃' => '⫃', + '⫄' => '⫄', + '⫅' => '⫅', + '⫅̸' => '&nsubE', + '⫆' => '⫆', + '⫆̸' => '&nsupseteqq', + '⫇' => '⫇', + '⫈' => '⫈', + '⫋' => '⫋', + '⫋︀' => '&vsubnE', + '⫌' => '⫌', + '⫌︀' => '&varsupsetneqq', + '⫏' => '⫏', + '⫐' => '⫐', + '⫑' => '⫑', + '⫒' => '⫒', + '⫓' => '⫓', + '⫔' => '⫔', + '⫕' => '⫕', + '⫖' => '⫖', + '⫗' => '⫗', + '⫘' => '⫘', + '⫙' => '⫙', + '⫚' => '⫚', + '⫛' => '⫛', + '⫤' => '⫤', + '⫦' => '⫦', + '⫧' => '⫧', + '⫨' => '⫨', + '⫩' => '⫩', + '⫫' => '⫫', + '⫬' => '⫬', + '⫭' => '⫭', + '⫮' => '⫮', + '⫯' => '⫯', + '⫰' => '⫰', + '⫱' => '⫱', + '⫲' => '⫲', + '⫳' => '⫳', + '⫽︀' => '&varsupsetneqq', + 'ff' => 'ff', + 'fi' => 'fi', + 'fl' => 'fl', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + '𝒜' => '𝒜', + '𝒞' => '𝒞', + '𝒟' => '𝒟', + '𝒢' => '𝒢', + '𝒥' => '𝒥', + '𝒦' => '𝒦', + '𝒩' => '𝒩', + '𝒪' => '𝒪', + '𝒫' => '𝒫', + '𝒬' => '𝒬', + '𝒮' => '𝒮', + '𝒯' => '𝒯', + '𝒰' => '𝒰', + '𝒱' => '𝒱', + '𝒲' => '𝒲', + '𝒳' => '𝒳', + '𝒴' => '𝒴', + '𝒵' => '𝒵', + '𝒶' => '𝒶', + '𝒷' => '𝒷', + '𝒸' => '𝒸', + '𝒹' => '𝒹', + '𝒻' => '𝒻', + '𝒽' => '𝒽', + '𝒾' => '𝒾', + '𝒿' => '𝒿', + '𝓀' => '𝓀', + '𝓁' => '𝓁', + '𝓂' => '𝓂', + '𝓃' => '𝓃', + '𝓅' => '𝓅', + '𝓆' => '𝓆', + '𝓇' => '𝓇', + '𝓈' => '𝓈', + '𝓉' => '𝓉', + '𝓊' => '𝓊', + '𝓋' => '𝓋', + '𝓌' => '𝓌', + '𝓍' => '𝓍', + '𝓎' => '𝓎', + '𝓏' => '𝓏', + '𝔄' => '𝔄', + '𝔅' => '𝔅', + '𝔇' => '𝔇', + '𝔈' => '𝔈', + '𝔉' => '𝔉', + '𝔊' => '𝔊', + '𝔍' => '𝔍', + '𝔎' => '𝔎', + '𝔏' => '𝔏', + '𝔐' => '𝔐', + '𝔑' => '𝔑', + '𝔒' => '𝔒', + '𝔓' => '𝔓', + '𝔔' => '𝔔', + '𝔖' => '𝔖', + '𝔗' => '𝔗', + '𝔘' => '𝔘', + '𝔙' => '𝔙', + '𝔚' => '𝔚', + '𝔛' => '𝔛', + '𝔜' => '𝔜', + '𝔞' => '𝔞', + '𝔟' => '𝔟', + '𝔠' => '𝔠', + '𝔡' => '𝔡', + '𝔢' => '𝔢', + '𝔣' => '𝔣', + '𝔤' => '𝔤', + '𝔥' => '𝔥', + '𝔦' => '𝔦', + '𝔧' => '𝔧', + '𝔨' => '𝔨', + '𝔩' => '𝔩', + '𝔪' => '𝔪', + '𝔫' => '𝔫', + '𝔬' => '𝔬', + '𝔭' => '𝔭', + '𝔮' => '𝔮', + '𝔯' => '𝔯', + '𝔰' => '𝔰', + '𝔱' => '𝔱', + '𝔲' => '𝔲', + '𝔳' => '𝔳', + '𝔴' => '𝔴', + '𝔵' => '𝔵', + '𝔶' => '𝔶', + '𝔷' => '𝔷', + '𝔸' => '𝔸', + '𝔹' => '𝔹', + '𝔻' => '𝔻', + '𝔼' => '𝔼', + '𝔽' => '𝔽', + '𝔾' => '𝔾', + '𝕀' => '𝕀', + '𝕁' => '𝕁', + '𝕂' => '𝕂', + '𝕃' => '𝕃', + '𝕄' => '𝕄', + '𝕆' => '𝕆', + '𝕊' => '𝕊', + '𝕋' => '𝕋', + '𝕌' => '𝕌', + '𝕍' => '𝕍', + '𝕎' => '𝕎', + '𝕏' => '𝕏', + '𝕐' => '𝕐', + '𝕒' => '𝕒', + '𝕓' => '𝕓', + '𝕔' => '𝕔', + '𝕕' => '𝕕', + '𝕖' => '𝕖', + '𝕗' => '𝕗', + '𝕘' => '𝕘', + '𝕙' => '𝕙', + '𝕚' => '𝕚', + '𝕛' => '𝕛', + '𝕜' => '𝕜', + '𝕝' => '𝕝', + '𝕞' => '𝕞', + '𝕟' => '𝕟', + '𝕠' => '𝕠', + '𝕡' => '𝕡', + '𝕢' => '𝕢', + '𝕣' => '𝕣', + '𝕤' => '𝕤', + '𝕥' => '𝕥', + '𝕦' => '𝕦', + '𝕧' => '𝕧', + '𝕨' => '𝕨', + '𝕩' => '𝕩', + '𝕪' => '𝕪', + '𝕫' => '𝕫', + ); +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/OutputRules.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/OutputRules.php new file mode 100644 index 000000000..ec467f22c --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/OutputRules.php @@ -0,0 +1,553 @@ +'http://www.w3.org/1999/xhtml', + 'attrNamespace'=>'http://www.w3.org/1999/xhtml', + + 'nodeName'=>'img', 'nodeName'=>array('img', 'a'), + 'attrName'=>'alt', 'attrName'=>array('title', 'alt'), + ), + */ + array( + 'nodeNamespace' => 'http://www.w3.org/1999/xhtml', + 'attrName' => array('href', + 'hreflang', + 'http-equiv', + 'icon', + 'id', + 'keytype', + 'kind', + 'label', + 'lang', + 'language', + 'list', + 'maxlength', + 'media', + 'method', + 'name', + 'placeholder', + 'rel', + 'rows', + 'rowspan', + 'sandbox', + 'spellcheck', + 'scope', + 'seamless', + 'shape', + 'size', + 'sizes', + 'span', + 'src', + 'srcdoc', + 'srclang', + 'srcset', + 'start', + 'step', + 'style', + 'summary', + 'tabindex', + 'target', + 'title', + 'type', + 'value', + 'width', + 'border', + 'charset', + 'cite', + 'class', + 'code', + 'codebase', + 'color', + 'cols', + 'colspan', + 'content', + 'coords', + 'data', + 'datetime', + 'default', + 'dir', + 'dirname', + 'enctype', + 'for', + 'form', + 'formaction', + 'headers', + 'height', + 'accept', + 'accept-charset', + 'accesskey', + 'action', + 'align', + 'alt', + 'bgcolor', + ), + ), + array( + 'nodeNamespace' => 'http://www.w3.org/1999/xhtml', + 'xpath' => 'starts-with(local-name(), \'data-\')', + ), + ); + + const DOCTYPE = ''; + + public function __construct($output, $options = array()) + { + if (isset($options['encode_entities'])) { + $this->encode = $options['encode_entities']; + } + + $this->outputMode = static::IM_IN_HTML; + $this->out = $output; + $this->hasHTML5 = defined('ENT_HTML5'); + } + + public function addRule(array $rule) + { + $this->nonBooleanAttributes[] = $rule; + } + + public function setTraverser(Traverser $traverser) + { + $this->traverser = $traverser; + + return $this; + } + + public function unsetTraverser() + { + $this->traverser = null; + + return $this; + } + + public function document($dom) + { + $this->doctype(); + if ($dom->documentElement) { + foreach ($dom->childNodes as $node) { + $this->traverser->node($node); + } + $this->nl(); + } + } + + protected function doctype() + { + $this->wr(static::DOCTYPE); + $this->nl(); + } + + public function element($ele) + { + $name = $ele->tagName; + + // Per spec: + // If the element has a declared namespace in the HTML, MathML or + // SVG namespaces, we use the lname instead of the tagName. + if ($this->traverser->isLocalElement($ele)) { + $name = $ele->localName; + } + + // If we are in SVG or MathML there is special handling. + // Using if/elseif instead of switch because it's faster in PHP. + if ('svg' == $name) { + $this->outputMode = static::IM_IN_SVG; + $name = Elements::normalizeSvgElement($name); + } elseif ('math' == $name) { + $this->outputMode = static::IM_IN_MATHML; + } + + $this->openTag($ele); + if (Elements::isA($name, Elements::TEXT_RAW)) { + foreach ($ele->childNodes as $child) { + if ($child instanceof \DOMCharacterData) { + $this->wr($child->data); + } elseif ($child instanceof \DOMElement) { + $this->element($child); + } + } + } else { + // Handle children. + if ($ele->hasChildNodes()) { + $this->traverser->children($ele->childNodes); + } + + // Close out the SVG or MathML special handling. + if ('svg' == $name || 'math' == $name) { + $this->outputMode = static::IM_IN_HTML; + } + } + + // If not unary, add a closing tag. + if (!Elements::isA($name, Elements::VOID_TAG)) { + $this->closeTag($ele); + } + } + + /** + * Write a text node. + * + * @param \DOMText $ele The text node to write. + */ + public function text($ele) + { + if (isset($ele->parentNode) && isset($ele->parentNode->tagName) && Elements::isA($ele->parentNode->localName, Elements::TEXT_RAW)) { + $this->wr($ele->data); + + return; + } + + // FIXME: This probably needs some flags set. + $this->wr($this->enc($ele->data)); + } + + public function cdata($ele) + { + // This encodes CDATA. + $this->wr($ele->ownerDocument->saveXML($ele)); + } + + public function comment($ele) + { + // These produce identical output. + // $this->wr(''); + $this->wr($ele->ownerDocument->saveXML($ele)); + } + + public function processorInstruction($ele) + { + $this->wr('wr($ele->target) + ->wr(' ') + ->wr($ele->data) + ->wr('?>'); + } + + /** + * Write the namespace attributes. + * + * @param \DOMNode $ele The element being written. + */ + protected function namespaceAttrs($ele) + { + if (!$this->xpath || $this->xpath->document !== $ele->ownerDocument) { + $this->xpath = new \DOMXPath($ele->ownerDocument); + } + + foreach ($this->xpath->query('namespace::*[not(.=../../namespace::*)]', $ele) as $nsNode) { + if (!in_array($nsNode->nodeValue, $this->implicitNamespaces)) { + $this->wr(' ')->wr($nsNode->nodeName)->wr('="')->wr($nsNode->nodeValue)->wr('"'); + } + } + } + + /** + * Write the opening tag. + * + * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the + * qualified name (8.3). + * + * @param \DOMNode $ele The element being written. + */ + protected function openTag($ele) + { + $this->wr('<')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName); + + $this->attrs($ele); + $this->namespaceAttrs($ele); + + if ($this->outputMode == static::IM_IN_HTML) { + $this->wr('>'); + } // If we are not in html mode we are in SVG, MathML, or XML embedded content. + else { + if ($ele->hasChildNodes()) { + $this->wr('>'); + } // If there are no children this is self closing. + else { + $this->wr(' />'); + } + } + } + + protected function attrs($ele) + { + // FIXME: Needs support for xml, xmlns, xlink, and namespaced elements. + if (!$ele->hasAttributes()) { + return $this; + } + + // TODO: Currently, this always writes name="value", and does not do + // value-less attributes. + $map = $ele->attributes; + $len = $map->length; + for ($i = 0; $i < $len; ++$i) { + $node = $map->item($i); + $val = $this->enc($node->value, true); + + // XXX: The spec says that we need to ensure that anything in + // the XML, XMLNS, or XLink NS's should use the canonical + // prefix. It seems that DOM does this for us already, but there + // may be exceptions. + $name = $node->nodeName; + + // Special handling for attributes in SVG and MathML. + // Using if/elseif instead of switch because it's faster in PHP. + if ($this->outputMode == static::IM_IN_SVG) { + $name = Elements::normalizeSvgAttribute($name); + } elseif ($this->outputMode == static::IM_IN_MATHML) { + $name = Elements::normalizeMathMlAttribute($name); + } + + $this->wr(' ')->wr($name); + + if ((isset($val) && '' !== $val) || $this->nonBooleanAttribute($node)) { + $this->wr('="')->wr($val)->wr('"'); + } + } + } + + protected function nonBooleanAttribute(\DOMAttr $attr) + { + $ele = $attr->ownerElement; + foreach ($this->nonBooleanAttributes as $rule) { + if (isset($rule['nodeNamespace']) && $rule['nodeNamespace'] !== $ele->namespaceURI) { + continue; + } + if (isset($rule['attNamespace']) && $rule['attNamespace'] !== $attr->namespaceURI) { + continue; + } + if (isset($rule['nodeName']) && !is_array($rule['nodeName']) && $rule['nodeName'] !== $ele->localName) { + continue; + } + if (isset($rule['nodeName']) && is_array($rule['nodeName']) && !in_array($ele->localName, $rule['nodeName'], true)) { + continue; + } + if (isset($rule['attrName']) && !is_array($rule['attrName']) && $rule['attrName'] !== $attr->localName) { + continue; + } + if (isset($rule['attrName']) && is_array($rule['attrName']) && !in_array($attr->localName, $rule['attrName'], true)) { + continue; + } + if (isset($rule['xpath'])) { + $xp = $this->getXPath($attr); + if (isset($rule['prefixes'])) { + foreach ($rule['prefixes'] as $nsPrefix => $ns) { + $xp->registerNamespace($nsPrefix, $ns); + } + } + if (!$xp->evaluate($rule['xpath'], $attr)) { + continue; + } + } + + return true; + } + + return false; + } + + private function getXPath(\DOMNode $node) + { + if (!$this->xpath) { + $this->xpath = new \DOMXPath($node->ownerDocument); + } + + return $this->xpath; + } + + /** + * Write the closing tag. + * + * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the + * qualified name (8.3). + * + * @param \DOMNode $ele The element being written. + */ + protected function closeTag($ele) + { + if ($this->outputMode == static::IM_IN_HTML || $ele->hasChildNodes()) { + $this->wr('wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName)->wr('>'); + } + } + + /** + * Write to the output. + * + * @param string $text The string to put into the output + * + * @return $this + */ + protected function wr($text) + { + fwrite($this->out, $text); + + return $this; + } + + /** + * Write a new line character. + * + * @return $this + */ + protected function nl() + { + fwrite($this->out, PHP_EOL); + + return $this; + } + + /** + * Encode text. + * + * When encode is set to false, the default value, the text passed in is + * escaped per section 8.3 of the html5 spec. For details on how text is + * escaped see the escape() method. + * + * When encoding is set to true the text is converted to named character + * references where appropriate. Section 8.1.4 Character references of the + * html5 spec refers to using named character references. This is useful for + * characters that can't otherwise legally be used in the text. + * + * The named character references are listed in section 8.5. + * + * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#named-character-references True encoding will turn all named character references into their entities. + * This includes such characters as +.# and many other common ones. By default + * encoding here will just escape &'<>". + * + * Note, PHP 5.4+ has better html5 encoding. + * + * @todo Use the Entities class in php 5.3 to have html5 entities. + * + * @param string $text Text to encode. + * @param bool $attribute True if we are encoding an attrubute, false otherwise. + * + * @return string The encoded text. + */ + protected function enc($text, $attribute = false) + { + // Escape the text rather than convert to named character references. + if (!$this->encode) { + return $this->escape($text, $attribute); + } + + // If we are in PHP 5.4+ we can use the native html5 entity functionality to + // convert the named character references. + + if ($this->hasHTML5) { + return htmlentities($text, ENT_HTML5 | ENT_SUBSTITUTE | ENT_QUOTES, 'UTF-8', false); + } // If a version earlier than 5.4 html5 entities are not entirely handled. + // This manually handles them. + else { + return strtr($text, HTML5Entities::$map); + } + } + + /** + * Escape test. + * + * According to the html5 spec section 8.3 Serializing HTML fragments, text + * within tags that are not style, script, xmp, iframe, noembed, and noframes + * need to be properly escaped. + * + * The & should be converted to &, no breaking space unicode characters + * converted to  , when in attribute mode the " should be converted to + * ", and when not in attribute mode the < and > should be converted to + * < and >. + * + * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#escapingString + * + * @param string $text Text to escape. + * @param bool $attribute True if we are escaping an attrubute, false otherwise. + */ + protected function escape($text, $attribute = false) + { + // Not using htmlspecialchars because, while it does escaping, it doesn't + // match the requirements of section 8.5. For example, it doesn't handle + // non-breaking spaces. + if ($attribute) { + $replace = array( + '"' => '"', + '&' => '&', + "\xc2\xa0" => ' ', + ); + } else { + $replace = array( + '<' => '<', + '>' => '>', + '&' => '&', + "\xc2\xa0" => ' ', + ); + } + + return strtr($text, $replace); + } +} diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/README.md b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/README.md new file mode 100644 index 000000000..849a47f3a --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/README.md @@ -0,0 +1,33 @@ +# The Serializer (Writer) Model + +The serializer roughly follows sections _8.1 Writing HTML documents_ and section +_8.3 Serializing HTML fragments_ by converting DOMDocument, DOMDocumentFragment, +and DOMNodeList into HTML5. + + [ HTML5 ] // Interface for saving. + || + [ Traverser ] // Walk the DOM + || + [ Rules ] // Convert DOM elements into strings. + || + [ HTML5 ] // HTML5 document or fragment in text. + + +## HTML5 Class + +Provides the top level interface for saving. + +## The Traverser + +Walks the DOM finding each element and passing it off to the output rules to +convert to HTML5. + +## Output Rules + +The output rules are defined in the RulesInterface which can have multiple +implementations. Currently, the OutputRules is the default implementation that +converts a DOM as is into HTML5. + +## HTML5 String + +The output of the process it HTML5 as a string or saved to a file. \ No newline at end of file diff --git a/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/RulesInterface.php b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/RulesInterface.php new file mode 100644 index 000000000..69a6ecdad --- /dev/null +++ b/library/vendor/dompdf/vendor/masterminds/html5/src/HTML5/Serializer/RulesInterface.php @@ -0,0 +1,99 @@ + 'html', + 'http://www.w3.org/1998/Math/MathML' => 'math', + 'http://www.w3.org/2000/svg' => 'svg', + ); + + protected $dom; + + protected $options; + + protected $encode = false; + + protected $rules; + + protected $out; + + /** + * Create a traverser. + * + * @param \DOMNode|\DOMNodeList $dom The document or node to traverse. + * @param resource $out A stream that allows writing. The traverser will output into this + * stream. + * @param array $options An array of options for the traverser as key/value pairs. These include: + * - encode_entities: A bool to specify if full encding should happen for all named + * charachter references. Defaults to false which escapes &'<>". + * - output_rules: The path to the class handling the output rules. + */ + public function __construct($dom, $out, RulesInterface $rules, $options = array()) + { + $this->dom = $dom; + $this->out = $out; + $this->rules = $rules; + $this->options = $options; + + $this->rules->setTraverser($this); + } + + /** + * Tell the traverser to walk the DOM. + * + * @return resource $out Returns the output stream. + */ + public function walk() + { + if ($this->dom instanceof \DOMDocument) { + $this->rules->document($this->dom); + } elseif ($this->dom instanceof \DOMDocumentFragment) { + // Document fragments are a special case. Only the children need to + // be serialized. + if ($this->dom->hasChildNodes()) { + $this->children($this->dom->childNodes); + } + } // If NodeList, loop + elseif ($this->dom instanceof \DOMNodeList) { + // If this is a NodeList of DOMDocuments this will not work. + $this->children($this->dom); + } // Else assume this is a DOMNode-like datastructure. + else { + $this->node($this->dom); + } + + return $this->out; + } + + /** + * Process a node in the DOM. + * + * @param mixed $node A node implementing \DOMNode. + */ + public function node($node) + { + // A listing of types is at http://php.net/manual/en/dom.constants.php + switch ($node->nodeType) { + case XML_ELEMENT_NODE: + $this->rules->element($node); + break; + case XML_TEXT_NODE: + $this->rules->text($node); + break; + case XML_CDATA_SECTION_NODE: + $this->rules->cdata($node); + break; + case XML_PI_NODE: + $this->rules->processorInstruction($node); + break; + case XML_COMMENT_NODE: + $this->rules->comment($node); + break; + // Currently we don't support embedding DTDs. + default: + //print ''; + break; + } + } + + /** + * Walk through all the nodes on a node list. + * + * @param \DOMNodeList $nl A list of child elements to walk through. + */ + public function children($nl) + { + foreach ($nl as $node) { + $this->node($node); + } + } + + /** + * Is an element local? + * + * @param mixed $ele An element that implement \DOMNode. + * + * @return bool true if local and false otherwise. + */ + public function isLocalElement($ele) + { + $uri = $ele->namespaceURI; + if (empty($uri)) { + return false; + } + + return isset(static::$local_ns[$uri]); + } +} diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/.github/workflows/phpunit.yml b/library/vendor/dompdf/vendor/phenx/php-font-lib/.github/workflows/phpunit.yml new file mode 100644 index 000000000..82eeb8529 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/.github/workflows/phpunit.yml @@ -0,0 +1,44 @@ +name: PHPUnit tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + php-version: + + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php-version: + - "5.4" + - "5.5" + - "5.6" + - "7.0" + - "7.1" + - "7.2" + - "7.3" + - "7.4" + - "8.0" + - "8.1" + + steps: + + - uses: actions/checkout@v2 + + - name: Install PHP + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + coverage: "none" + ini-values: "zend.assertions=1" + + - name: Install Composer dependencies + run: composer install --no-progress --ansi + + - name: Run tests ${{ matrix.php-version }} + run: SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 bin/simple-phpunit --color=always diff --git a/library/vendor/dompdf/lib/php-font-lib/LICENSE b/library/vendor/dompdf/vendor/phenx/php-font-lib/LICENSE similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/LICENSE rename to library/vendor/dompdf/vendor/phenx/php-font-lib/LICENSE diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/README.md b/library/vendor/dompdf/vendor/phenx/php-font-lib/README.md new file mode 100644 index 000000000..0fa24ee6b --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/README.md @@ -0,0 +1,28 @@ +[![PHPUnit tests](https://github.com/dompdf/php-font-lib/actions/workflows/phpunit.yml/badge.svg)](https://github.com/dompdf/php-font-lib/actions/workflows/phpunit.yml) + +# PHP Font Lib + +This library can be used to: + * Read TrueType, OpenType (with TrueType glyphs), WOFF font files + * Extract basic info (name, style, etc) + * Extract advanced info (horizontal metrics, glyph names, glyph shapes, etc) + * Make an Adobe Font Metrics (AFM) file from a font + +You can find a demo GUI [here](http://pxd.me/php-font-lib/www/font_explorer.html). + +This project was initiated by the need to read font files in the [DOMPDF project](https://github.com/dompdf/dompdf). + +Usage Example +------------- + +``` +$font = \FontLib\Font::load('../../fontfile.ttf'); +$font->parse(); // for getFontWeight() to work this call must be done first! +echo $font->getFontName() .'
'; +echo $font->getFontSubfamily() .'
'; +echo $font->getFontSubfamilyID() .'
'; +echo $font->getFontFullName() .'
'; +echo $font->getFontVersion() .'
'; +echo $font->getFontWeight() .'
'; +echo $font->getFontPostscriptName() .'
'; +``` diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/bower.json b/library/vendor/dompdf/vendor/phenx/php-font-lib/bower.json new file mode 100644 index 000000000..0a4a45b2e --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/bower.json @@ -0,0 +1,23 @@ +{ + "name": "php-font-lib", + "version": "0.3.1", + "license": "LGPL-3.0", + "keywords": [ + "font", + "parse", + "export", + "truetype", + "opentype", + "woff" + ], + "homepage": "https://github.com/PhenX/php-font-lib", + "_release": "0.3.1", + "_resolution": { + "type": "version", + "tag": "v0.3.1", + "commit": "d13682b7e27d14a6323c441426f3dde1cd86c751" + }, + "_source": "https://github.com/PhenX/php-font-lib.git", + "_target": "*", + "_originalSource": "https://github.com/PhenX/php-font-lib.git" +} \ No newline at end of file diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/composer.json b/library/vendor/dompdf/vendor/phenx/php-font-lib/composer.json new file mode 100644 index 000000000..29b0653a1 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/composer.json @@ -0,0 +1,32 @@ +{ + "name": "phenx/php-font-lib", + "type": "library", + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/PhenX/php-font-lib", + "license": "LGPL-3.0", + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "autoload-dev": { + "psr-4": { + "FontLib\\Tests\\": "tests/FontLib" + } + }, + "config": { + "bin-dir": "bin" + }, + "require": { + "ext-mbstring": "*" + }, + "require-dev": { + "symfony/phpunit-bridge" : "^3 || ^4 || ^5" + } +} diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/index.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/index.php new file mode 100644 index 000000000..7ed173a89 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/index.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/adobe-standard-encoding.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/adobe-standard-encoding.map new file mode 100644 index 000000000..230d4a1ef --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/adobe-standard-encoding.map @@ -0,0 +1,231 @@ +// Adobe Standard Encoding table for ttf2pt1 +// Thomas Henlich + +=20 U+0020 SPACE +=21 U+0021 EXCLAMATION MARK +=22 U+0022 QUOTATION MARK +=23 U+0023 NUMBER SIGN +=24 U+0024 DOLLAR SIGN +=25 U+0025 PERCENT SIGN +=26 U+0026 AMPERSAND +=27 U+2019 RIGHT SINGLE QUOTATION MARK +=28 U+0028 LEFT PARENTHESIS +=29 U+0029 RIGHT PARENTHESIS +=2A U+002A ASTERISK +=2B U+002B PLUS SIGN +=2C U+002C COMMA +=2D U+002D HYPHEN-MINUS +=2E U+002E FULL STOP +=2F U+002F SOLIDUS +=30 U+0030 DIGIT ZERO +=31 U+0031 DIGIT ONE +=32 U+0032 DIGIT TWO +=33 U+0033 DIGIT THREE +=34 U+0034 DIGIT FOUR +=35 U+0035 DIGIT FIVE +=36 U+0036 DIGIT SIX +=37 U+0037 DIGIT SEVEN +=38 U+0038 DIGIT EIGHT +=39 U+0039 DIGIT NINE +=3A U+003A COLON +=3B U+003B SEMICOLON +=3C U+003C LESS-THAN SIGN +=3D U+003D EQUALS SIGN +=3E U+003E GREATER-THAN SIGN +=3F U+003F QUESTION MARK +=40 U+0040 COMMERCIAL AT +=41 U+0041 LATIN CAPITAL LETTER A +=42 U+0042 LATIN CAPITAL LETTER B +=43 U+0043 LATIN CAPITAL LETTER C +=44 U+0044 LATIN CAPITAL LETTER D +=45 U+0045 LATIN CAPITAL LETTER E +=46 U+0046 LATIN CAPITAL LETTER F +=47 U+0047 LATIN CAPITAL LETTER G +=48 U+0048 LATIN CAPITAL LETTER H +=49 U+0049 LATIN CAPITAL LETTER I +=4A U+004A LATIN CAPITAL LETTER J +=4B U+004B LATIN CAPITAL LETTER K +=4C U+004C LATIN CAPITAL LETTER L +=4D U+004D LATIN CAPITAL LETTER M +=4E U+004E LATIN CAPITAL LETTER N +=4F U+004F LATIN CAPITAL LETTER O +=50 U+0050 LATIN CAPITAL LETTER P +=51 U+0051 LATIN CAPITAL LETTER Q +=52 U+0052 LATIN CAPITAL LETTER R +=53 U+0053 LATIN CAPITAL LETTER S +=54 U+0054 LATIN CAPITAL LETTER T +=55 U+0055 LATIN CAPITAL LETTER U +=56 U+0056 LATIN CAPITAL LETTER V +=57 U+0057 LATIN CAPITAL LETTER W +=58 U+0058 LATIN CAPITAL LETTER X +=59 U+0059 LATIN CAPITAL LETTER Y +=5A U+005A LATIN CAPITAL LETTER Z +=5B U+005B LEFT SQUARE BRACKET +=5C U+005C REVERSE SOLIDUS +=5D U+005D RIGHT SQUARE BRACKET +=5E U+005E CIRCUMFLEX ACCENT +=5F U+005F LOW LINE +=60 U+2018 LEFT SINGLE QUOTATION MARK +=61 U+0061 LATIN SMALL LETTER A +=62 U+0062 LATIN SMALL LETTER B +=63 U+0063 LATIN SMALL LETTER C +=64 U+0064 LATIN SMALL LETTER D +=65 U+0065 LATIN SMALL LETTER E +=66 U+0066 LATIN SMALL LETTER F +=67 U+0067 LATIN SMALL LETTER G +=68 U+0068 LATIN SMALL LETTER H +=69 U+0069 LATIN SMALL LETTER I +=6A U+006A LATIN SMALL LETTER J +=6B U+006B LATIN SMALL LETTER K +=6C U+006C LATIN SMALL LETTER L +=6D U+006D LATIN SMALL LETTER M +=6E U+006E LATIN SMALL LETTER N +=6F U+006F LATIN SMALL LETTER O +=70 U+0070 LATIN SMALL LETTER P +=71 U+0071 LATIN SMALL LETTER Q +=72 U+0072 LATIN SMALL LETTER R +=73 U+0073 LATIN SMALL LETTER S +=74 U+0074 LATIN SMALL LETTER T +=75 U+0075 LATIN SMALL LETTER U +=76 U+0076 LATIN SMALL LETTER V +=77 U+0077 LATIN SMALL LETTER W +=78 U+0078 LATIN SMALL LETTER X +=79 U+0079 LATIN SMALL LETTER Y +=7A U+007A LATIN SMALL LETTER Z +=7B U+007B LEFT CURLY BRACKET +=7C U+007C VERTICAL LINE +=7D U+007D RIGHT CURLY BRACKET +=7E U+007E TILDE +=A1 U+00A1 INVERTED EXCLAMATION MARK +=A2 U+00A2 CENT SIGN +=A3 U+00A3 POUND SIGN +=A4 U+2044 FRACTION SLASH +=A5 U+00A5 YEN SIGN +=A6 U+0192 LATIN SMALL LETTER F WITH HOOK +=A7 U+00A7 SECTION SIGN +=A8 U+00A4 CURRENCY SIGN +=A9 U+0027 APOSTROPHE +=AA U+201C LEFT DOUBLE QUOTATION MARK +=AB U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +=AC U+2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK +=AD U+203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +=AE U+FB01 LATIN SMALL LIGATURE FI +=AF U+FB02 LATIN SMALL LIGATURE FL +=B1 U+2013 EN DASH +=B2 U+2020 DAGGER +=B3 U+2021 DOUBLE DAGGER +=B4 U+00B7 MIDDLE DOT +=B6 U+00B6 PILCROW SIGN +=B7 U+2022 BULLET +=B8 U+201A SINGLE LOW-9 QUOTATION MARK +=B9 U+201E DOUBLE LOW-9 QUOTATION MARK +=BA U+201D RIGHT DOUBLE QUOTATION MARK +=BB U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +=BC U+2026 HORIZONTAL ELLIPSIS +=BD U+2030 PER MILLE SIGN +=BF U+00BF INVERTED QUESTION MARK +=C1 U+0060 GRAVE ACCENT +=C2 U+00B4 ACUTE ACCENT +=C3 U+02C6 MODIFIER LETTER CIRCUMFLEX ACCENT +=C4 U+02DC SMALL TILDE +=C5 U+00AF MACRON +=C6 U+02D8 BREVE +=C7 U+02D9 DOT ABOVE +=C8 U+00A8 DIAERESIS +=CA U+02DA RING ABOVE +=CB U+00B8 CEDILLA +=CD U+02DD DOUBLE ACUTE ACCENT +=CE U+02DB OGONEK +=CF U+02C7 CARON +=D0 U+2014 EM DASH +=E1 U+00C6 LATIN CAPITAL LETTER AE +=E3 U+00AA FEMININE ORDINAL INDICATOR +=E8 U+0141 LATIN CAPITAL LETTER L WITH STROKE +=E9 U+00D8 LATIN CAPITAL LETTER O WITH STROKE +=EA U+0152 LATIN CAPITAL LIGATURE OE +=EB U+00BA MASCULINE ORDINAL INDICATOR +=F1 U+00E6 LATIN SMALL LETTER AE +=F5 U+0131 LATIN SMALL LETTER DOTLESS I +=F8 U+0142 LATIN SMALL LETTER L WITH STROKE +=F9 U+00F8 LATIN SMALL LETTER O WITH STROKE +=FA U+0153 LATIN SMALL LIGATURE OE +=FB U+00DF LATIN SMALL LETTER SHARP S + +// unencoded characters: +=100 U+00E7 LATIN SMALL LETTER C WITH CEDILLA +=101 U+00FF LATIN SMALL LETTER Y WITH DIAERESIS +=102 U+00E3 LATIN SMALL LETTER A WITH TILDE +=103 U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX +=104 U+00B3 SUPERSCRIPT THREE +=105 U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX +=106 U+00FE LATIN SMALL LETTER THORN +=107 U+00E8 LATIN SMALL LETTER E WITH GRAVE +=108 U+00B2 SUPERSCRIPT TWO +=109 U+00E9 LATIN SMALL LETTER E WITH ACUTE +=10A U+00F5 LATIN SMALL LETTER O WITH TILDE +=10B U+00C1 LATIN CAPITAL LETTER A WITH ACUTE +=10C U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX +=10D U+00FD LATIN SMALL LETTER Y WITH ACUTE +=10E U+00FC LATIN SMALL LETTER U WITH DIAERESIS +=10F U+00BE VULGAR FRACTION THREE QUARTERS +=110 U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX +=111 U+00D0 LATIN CAPITAL LETTER ETH +=112 U+00EB LATIN SMALL LETTER E WITH DIAERESIS +=113 U+00F9 LATIN SMALL LETTER U WITH GRAVE +=114 U+2122 TRADE MARK SIGN +=115 U+00F2 LATIN SMALL LETTER O WITH GRAVE +=116 U+0161 LATIN SMALL LETTER S WITH CARON +=117 U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS +=118 U+00FA LATIN SMALL LETTER U WITH ACUTE +=119 U+00E0 LATIN SMALL LETTER A WITH GRAVE +=11A U+00F1 LATIN SMALL LETTER N WITH TILDE +=11B U+00E5 LATIN SMALL LETTER A WITH RING ABOVE +=11C U+017E LATIN SMALL LETTER Z WITH CARON +=11D U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX +=11E U+00D1 LATIN CAPITAL LETTER N WITH TILDE +=11F U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX +=120 U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX +=121 U+00CD LATIN CAPITAL LETTER I WITH ACUTE +=122 U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA +=123 U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS +=124 U+0160 LATIN CAPITAL LETTER S WITH CARON +=125 U+00CC LATIN CAPITAL LETTER I WITH GRAVE +=126 U+00E4 LATIN SMALL LETTER A WITH DIAERESIS +=127 U+00D2 LATIN CAPITAL LETTER O WITH GRAVE +=128 U+00C8 LATIN CAPITAL LETTER E WITH GRAVE +=129 U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS +=12A U+00AE REGISTERED SIGN +=12B U+00D5 LATIN CAPITAL LETTER O WITH TILDE +=12C U+00BC VULGAR FRACTION ONE QUARTER +=12D U+00D9 LATIN CAPITAL LETTER U WITH GRAVE +=12E U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX +=12F U+00DE LATIN CAPITAL LETTER THORN +=130 U+00F7 DIVISION SIGN +=131 U+00C3 LATIN CAPITAL LETTER A WITH TILDE +=132 U+00DA LATIN CAPITAL LETTER U WITH ACUTE +=133 U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX +=134 U+00AC NOT SIGN +=135 U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE +=136 U+00EF LATIN SMALL LETTER I WITH DIAERESIS +=137 U+00ED LATIN SMALL LETTER I WITH ACUTE +=138 U+00E1 LATIN SMALL LETTER A WITH ACUTE +=139 U+00B1 PLUS-MINUS SIGN +=13A U+00D7 MULTIPLICATION SIGN +=13B U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS +=13C U+2212 MINUS SIGN +=13D U+00B9 SUPERSCRIPT ONE +=13E U+00C9 LATIN CAPITAL LETTER E WITH ACUTE +=13F U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX +=140 U+00A9 COPYRIGHT SIGN +=141 U+00C0 LATIN CAPITAL LETTER A WITH GRAVE +=142 U+00F6 LATIN SMALL LETTER O WITH DIAERESIS +=143 U+00F3 LATIN SMALL LETTER O WITH ACUTE +=144 U+00B0 DEGREE SIGN +=145 U+00EC LATIN SMALL LETTER I WITH GRAVE +=146 U+00B5 MICRO SIGN +=147 U+00D3 LATIN CAPITAL LETTER O WITH ACUTE +=148 U+00F0 LATIN SMALL LETTER ETH +=149 U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS +=14A U+00DD LATIN CAPITAL LETTER Y WITH ACUTE +=14B U+00A6 BROKEN BAR +=14C U+00BD VULGAR FRACTION ONE HALF diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1250.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1250.map new file mode 100644 index 000000000..33a555eeb --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1250.map @@ -0,0 +1,251 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+015A Sacute +!8D U+0164 Tcaron +!8E U+017D Zcaron +!8F U+0179 Zacute +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+015B sacute +!9D U+0165 tcaron +!9E U+017E zcaron +!9F U+017A zacute +!A0 U+00A0 space +!A1 U+02C7 caron +!A2 U+02D8 breve +!A3 U+0141 Lslash +!A4 U+00A4 currency +!A5 U+0104 Aogonek +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+015E Scedilla +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+02DB ogonek +!B3 U+0142 lslash +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+0105 aogonek +!BA U+015F scedilla +!BB U+00BB guillemotright +!BC U+013D Lcaron +!BD U+02DD hungarumlaut +!BE U+013E lcaron +!BF U+017C zdotaccent +!C0 U+0154 Racute +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0139 Lacute +!C6 U+0106 Cacute +!C7 U+00C7 Ccedilla +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+011A Ecaron +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+010E Dcaron +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+0147 Ncaron +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0158 Rcaron +!D9 U+016E Uring +!DA U+00DA Uacute +!DB U+0170 Uhungarumlaut +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+0162 Tcommaaccent +!DF U+00DF germandbls +!E0 U+0155 racute +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+013A lacute +!E6 U+0107 cacute +!E7 U+00E7 ccedilla +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+011B ecaron +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+010F dcaron +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+0148 ncaron +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0159 rcaron +!F9 U+016F uring +!FA U+00FA uacute +!FB U+0171 uhungarumlaut +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+0163 tcommaaccent +!FF U+02D9 dotaccent diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1251.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1251.map new file mode 100644 index 000000000..b5960fe4c --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1251.map @@ -0,0 +1,255 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0402 afii10051 +!81 U+0403 afii10052 +!82 U+201A quotesinglbase +!83 U+0453 afii10100 +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+20AC Euro +!89 U+2030 perthousand +!8A U+0409 afii10058 +!8B U+2039 guilsinglleft +!8C U+040A afii10059 +!8D U+040C afii10061 +!8E U+040B afii10060 +!8F U+040F afii10145 +!90 U+0452 afii10099 +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9A U+0459 afii10106 +!9B U+203A guilsinglright +!9C U+045A afii10107 +!9D U+045C afii10109 +!9E U+045B afii10108 +!9F U+045F afii10193 +!A0 U+00A0 space +!A1 U+040E afii10062 +!A2 U+045E afii10110 +!A3 U+0408 afii10057 +!A4 U+00A4 currency +!A5 U+0490 afii10050 +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+0401 afii10023 +!A9 U+00A9 copyright +!AA U+0404 afii10053 +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+0407 afii10056 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+0406 afii10055 +!B3 U+0456 afii10103 +!B4 U+0491 afii10098 +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+0451 afii10071 +!B9 U+2116 afii61352 +!BA U+0454 afii10101 +!BB U+00BB guillemotright +!BC U+0458 afii10105 +!BD U+0405 afii10054 +!BE U+0455 afii10102 +!BF U+0457 afii10104 +!C0 U+0410 afii10017 +!C1 U+0411 afii10018 +!C2 U+0412 afii10019 +!C3 U+0413 afii10020 +!C4 U+0414 afii10021 +!C5 U+0415 afii10022 +!C6 U+0416 afii10024 +!C7 U+0417 afii10025 +!C8 U+0418 afii10026 +!C9 U+0419 afii10027 +!CA U+041A afii10028 +!CB U+041B afii10029 +!CC U+041C afii10030 +!CD U+041D afii10031 +!CE U+041E afii10032 +!CF U+041F afii10033 +!D0 U+0420 afii10034 +!D1 U+0421 afii10035 +!D2 U+0422 afii10036 +!D3 U+0423 afii10037 +!D4 U+0424 afii10038 +!D5 U+0425 afii10039 +!D6 U+0426 afii10040 +!D7 U+0427 afii10041 +!D8 U+0428 afii10042 +!D9 U+0429 afii10043 +!DA U+042A afii10044 +!DB U+042B afii10045 +!DC U+042C afii10046 +!DD U+042D afii10047 +!DE U+042E afii10048 +!DF U+042F afii10049 +!E0 U+0430 afii10065 +!E1 U+0431 afii10066 +!E2 U+0432 afii10067 +!E3 U+0433 afii10068 +!E4 U+0434 afii10069 +!E5 U+0435 afii10070 +!E6 U+0436 afii10072 +!E7 U+0437 afii10073 +!E8 U+0438 afii10074 +!E9 U+0439 afii10075 +!EA U+043A afii10076 +!EB U+043B afii10077 +!EC U+043C afii10078 +!ED U+043D afii10079 +!EE U+043E afii10080 +!EF U+043F afii10081 +!F0 U+0440 afii10082 +!F1 U+0441 afii10083 +!F2 U+0442 afii10084 +!F3 U+0443 afii10085 +!F4 U+0444 afii10086 +!F5 U+0445 afii10087 +!F6 U+0446 afii10088 +!F7 U+0447 afii10089 +!F8 U+0448 afii10090 +!F9 U+0449 afii10091 +!FA U+044A afii10092 +!FB U+044B afii10093 +!FC U+044C afii10094 +!FD U+044D afii10095 +!FE U+044E afii10096 +!FF U+044F afii10097 diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1252.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1252.map new file mode 100644 index 000000000..b79015c31 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1252.map @@ -0,0 +1,251 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+0152 OE +!8E U+017D Zcaron +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+0153 oe +!9E U+017E zcaron +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1253.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1253.map new file mode 100644 index 000000000..b5d843c1b --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1253.map @@ -0,0 +1,239 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9B U+203A guilsinglright +!A0 U+00A0 space +!A1 U+0385 dieresistonos +!A2 U+0386 Alphatonos +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+2015 afii00208 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+0384 tonos +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+0388 Epsilontonos +!B9 U+0389 Etatonos +!BA U+038A Iotatonos +!BB U+00BB guillemotright +!BC U+038C Omicrontonos +!BD U+00BD onehalf +!BE U+038E Upsilontonos +!BF U+038F Omegatonos +!C0 U+0390 iotadieresistonos +!C1 U+0391 Alpha +!C2 U+0392 Beta +!C3 U+0393 Gamma +!C4 U+0394 Delta +!C5 U+0395 Epsilon +!C6 U+0396 Zeta +!C7 U+0397 Eta +!C8 U+0398 Theta +!C9 U+0399 Iota +!CA U+039A Kappa +!CB U+039B Lambda +!CC U+039C Mu +!CD U+039D Nu +!CE U+039E Xi +!CF U+039F Omicron +!D0 U+03A0 Pi +!D1 U+03A1 Rho +!D3 U+03A3 Sigma +!D4 U+03A4 Tau +!D5 U+03A5 Upsilon +!D6 U+03A6 Phi +!D7 U+03A7 Chi +!D8 U+03A8 Psi +!D9 U+03A9 Omega +!DA U+03AA Iotadieresis +!DB U+03AB Upsilondieresis +!DC U+03AC alphatonos +!DD U+03AD epsilontonos +!DE U+03AE etatonos +!DF U+03AF iotatonos +!E0 U+03B0 upsilondieresistonos +!E1 U+03B1 alpha +!E2 U+03B2 beta +!E3 U+03B3 gamma +!E4 U+03B4 delta +!E5 U+03B5 epsilon +!E6 U+03B6 zeta +!E7 U+03B7 eta +!E8 U+03B8 theta +!E9 U+03B9 iota +!EA U+03BA kappa +!EB U+03BB lambda +!EC U+03BC mu +!ED U+03BD nu +!EE U+03BE xi +!EF U+03BF omicron +!F0 U+03C0 pi +!F1 U+03C1 rho +!F2 U+03C2 sigma1 +!F3 U+03C3 sigma +!F4 U+03C4 tau +!F5 U+03C5 upsilon +!F6 U+03C6 phi +!F7 U+03C7 chi +!F8 U+03C8 psi +!F9 U+03C9 omega +!FA U+03CA iotadieresis +!FB U+03CB upsilondieresis +!FC U+03CC omicrontonos +!FD U+03CD upsilontonos +!FE U+03CE omegatonos diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1254.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1254.map new file mode 100644 index 000000000..3cc8c7894 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1254.map @@ -0,0 +1,249 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+0152 OE +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+0153 oe +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+011E Gbreve +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0130 Idotaccent +!DE U+015E Scedilla +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+011F gbreve +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0131 dotlessi +!FE U+015F scedilla +!FF U+00FF ydieresis diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1255.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1255.map new file mode 100644 index 000000000..fa135306d --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1255.map @@ -0,0 +1,233 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9B U+203A guilsinglright +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+20AA afii57636 +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00D7 multiply +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD sfthyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 middot +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00F7 divide +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+05B0 afii57799 +!C1 U+05B1 afii57801 +!C2 U+05B2 afii57800 +!C3 U+05B3 afii57802 +!C4 U+05B4 afii57793 +!C5 U+05B5 afii57794 +!C6 U+05B6 afii57795 +!C7 U+05B7 afii57798 +!C8 U+05B8 afii57797 +!C9 U+05B9 afii57806 +!CB U+05BB afii57796 +!CC U+05BC afii57807 +!CD U+05BD afii57839 +!CE U+05BE afii57645 +!CF U+05BF afii57841 +!D0 U+05C0 afii57842 +!D1 U+05C1 afii57804 +!D2 U+05C2 afii57803 +!D3 U+05C3 afii57658 +!D4 U+05F0 afii57716 +!D5 U+05F1 afii57717 +!D6 U+05F2 afii57718 +!D7 U+05F3 gereshhebrew +!D8 U+05F4 gershayimhebrew +!E0 U+05D0 afii57664 +!E1 U+05D1 afii57665 +!E2 U+05D2 afii57666 +!E3 U+05D3 afii57667 +!E4 U+05D4 afii57668 +!E5 U+05D5 afii57669 +!E6 U+05D6 afii57670 +!E7 U+05D7 afii57671 +!E8 U+05D8 afii57672 +!E9 U+05D9 afii57673 +!EA U+05DA afii57674 +!EB U+05DB afii57675 +!EC U+05DC afii57676 +!ED U+05DD afii57677 +!EE U+05DE afii57678 +!EF U+05DF afii57679 +!F0 U+05E0 afii57680 +!F1 U+05E1 afii57681 +!F2 U+05E2 afii57682 +!F3 U+05E3 afii57683 +!F4 U+05E4 afii57684 +!F5 U+05E5 afii57685 +!F6 U+05E6 afii57686 +!F7 U+05E7 afii57687 +!F8 U+05E8 afii57688 +!F9 U+05E9 afii57689 +!FA U+05EA afii57690 +!FD U+200E afii299 +!FE U+200F afii300 diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1257.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1257.map new file mode 100644 index 000000000..077fdc34a --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1257.map @@ -0,0 +1,244 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!8D U+00A8 dieresis +!8E U+02C7 caron +!8F U+00B8 cedilla +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9B U+203A guilsinglright +!9D U+00AF macron +!9E U+02DB ogonek +!A0 U+00A0 space +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00D8 Oslash +!A9 U+00A9 copyright +!AA U+0156 Rcommaaccent +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00C6 AE +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00F8 oslash +!B9 U+00B9 onesuperior +!BA U+0157 rcommaaccent +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00E6 ae +!C0 U+0104 Aogonek +!C1 U+012E Iogonek +!C2 U+0100 Amacron +!C3 U+0106 Cacute +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+0118 Eogonek +!C7 U+0112 Emacron +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0179 Zacute +!CB U+0116 Edotaccent +!CC U+0122 Gcommaaccent +!CD U+0136 Kcommaaccent +!CE U+012A Imacron +!CF U+013B Lcommaaccent +!D0 U+0160 Scaron +!D1 U+0143 Nacute +!D2 U+0145 Ncommaaccent +!D3 U+00D3 Oacute +!D4 U+014C Omacron +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0172 Uogonek +!D9 U+0141 Lslash +!DA U+015A Sacute +!DB U+016A Umacron +!DC U+00DC Udieresis +!DD U+017B Zdotaccent +!DE U+017D Zcaron +!DF U+00DF germandbls +!E0 U+0105 aogonek +!E1 U+012F iogonek +!E2 U+0101 amacron +!E3 U+0107 cacute +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+0119 eogonek +!E7 U+0113 emacron +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+017A zacute +!EB U+0117 edotaccent +!EC U+0123 gcommaaccent +!ED U+0137 kcommaaccent +!EE U+012B imacron +!EF U+013C lcommaaccent +!F0 U+0161 scaron +!F1 U+0144 nacute +!F2 U+0146 ncommaaccent +!F3 U+00F3 oacute +!F4 U+014D omacron +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0173 uogonek +!F9 U+0142 lslash +!FA U+015B sacute +!FB U+016B umacron +!FC U+00FC udieresis +!FD U+017C zdotaccent +!FE U+017E zcaron +!FF U+02D9 dotaccent diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1258.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1258.map new file mode 100644 index 000000000..7032c35d6 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp1258.map @@ -0,0 +1,247 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!8C U+0152 OE +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9B U+203A guilsinglright +!9C U+0153 oe +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+0300 gravecomb +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+0110 Dcroat +!D1 U+00D1 Ntilde +!D2 U+0309 hookabovecomb +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+01A0 Ohorn +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+01AF Uhorn +!DE U+0303 tildecomb +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+0301 acutecomb +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+0111 dcroat +!F1 U+00F1 ntilde +!F2 U+0323 dotbelowcomb +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+01A1 ohorn +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+01B0 uhorn +!FE U+20AB dong +!FF U+00FF ydieresis diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp874.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp874.map new file mode 100644 index 000000000..16330d042 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/cp874.map @@ -0,0 +1,225 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!85 U+2026 ellipsis +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!A0 U+00A0 space +!A1 U+0E01 kokaithai +!A2 U+0E02 khokhaithai +!A3 U+0E03 khokhuatthai +!A4 U+0E04 khokhwaithai +!A5 U+0E05 khokhonthai +!A6 U+0E06 khorakhangthai +!A7 U+0E07 ngonguthai +!A8 U+0E08 chochanthai +!A9 U+0E09 chochingthai +!AA U+0E0A chochangthai +!AB U+0E0B sosothai +!AC U+0E0C chochoethai +!AD U+0E0D yoyingthai +!AE U+0E0E dochadathai +!AF U+0E0F topatakthai +!B0 U+0E10 thothanthai +!B1 U+0E11 thonangmonthothai +!B2 U+0E12 thophuthaothai +!B3 U+0E13 nonenthai +!B4 U+0E14 dodekthai +!B5 U+0E15 totaothai +!B6 U+0E16 thothungthai +!B7 U+0E17 thothahanthai +!B8 U+0E18 thothongthai +!B9 U+0E19 nonuthai +!BA U+0E1A bobaimaithai +!BB U+0E1B poplathai +!BC U+0E1C phophungthai +!BD U+0E1D fofathai +!BE U+0E1E phophanthai +!BF U+0E1F fofanthai +!C0 U+0E20 phosamphaothai +!C1 U+0E21 momathai +!C2 U+0E22 yoyakthai +!C3 U+0E23 roruathai +!C4 U+0E24 ruthai +!C5 U+0E25 lolingthai +!C6 U+0E26 luthai +!C7 U+0E27 wowaenthai +!C8 U+0E28 sosalathai +!C9 U+0E29 sorusithai +!CA U+0E2A sosuathai +!CB U+0E2B hohipthai +!CC U+0E2C lochulathai +!CD U+0E2D oangthai +!CE U+0E2E honokhukthai +!CF U+0E2F paiyannoithai +!D0 U+0E30 saraathai +!D1 U+0E31 maihanakatthai +!D2 U+0E32 saraaathai +!D3 U+0E33 saraamthai +!D4 U+0E34 saraithai +!D5 U+0E35 saraiithai +!D6 U+0E36 sarauethai +!D7 U+0E37 saraueethai +!D8 U+0E38 sarauthai +!D9 U+0E39 sarauuthai +!DA U+0E3A phinthuthai +!DF U+0E3F bahtthai +!E0 U+0E40 saraethai +!E1 U+0E41 saraaethai +!E2 U+0E42 saraothai +!E3 U+0E43 saraaimaimuanthai +!E4 U+0E44 saraaimaimalaithai +!E5 U+0E45 lakkhangyaothai +!E6 U+0E46 maiyamokthai +!E7 U+0E47 maitaikhuthai +!E8 U+0E48 maiekthai +!E9 U+0E49 maithothai +!EA U+0E4A maitrithai +!EB U+0E4B maichattawathai +!EC U+0E4C thanthakhatthai +!ED U+0E4D nikhahitthai +!EE U+0E4E yamakkanthai +!EF U+0E4F fongmanthai +!F0 U+0E50 zerothai +!F1 U+0E51 onethai +!F2 U+0E52 twothai +!F3 U+0E53 threethai +!F4 U+0E54 fourthai +!F5 U+0E55 fivethai +!F6 U+0E56 sixthai +!F7 U+0E57 seventhai +!F8 U+0E58 eightthai +!F9 U+0E59 ninethai +!FA U+0E5A angkhankhuthai +!FB U+0E5B khomutthai diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-1.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-1.map new file mode 100644 index 000000000..74fb2fe5c --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-1.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-11.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-11.map new file mode 100644 index 000000000..8cf66731a --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-11.map @@ -0,0 +1,248 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0E01 kokaithai +!A2 U+0E02 khokhaithai +!A3 U+0E03 khokhuatthai +!A4 U+0E04 khokhwaithai +!A5 U+0E05 khokhonthai +!A6 U+0E06 khorakhangthai +!A7 U+0E07 ngonguthai +!A8 U+0E08 chochanthai +!A9 U+0E09 chochingthai +!AA U+0E0A chochangthai +!AB U+0E0B sosothai +!AC U+0E0C chochoethai +!AD U+0E0D yoyingthai +!AE U+0E0E dochadathai +!AF U+0E0F topatakthai +!B0 U+0E10 thothanthai +!B1 U+0E11 thonangmonthothai +!B2 U+0E12 thophuthaothai +!B3 U+0E13 nonenthai +!B4 U+0E14 dodekthai +!B5 U+0E15 totaothai +!B6 U+0E16 thothungthai +!B7 U+0E17 thothahanthai +!B8 U+0E18 thothongthai +!B9 U+0E19 nonuthai +!BA U+0E1A bobaimaithai +!BB U+0E1B poplathai +!BC U+0E1C phophungthai +!BD U+0E1D fofathai +!BE U+0E1E phophanthai +!BF U+0E1F fofanthai +!C0 U+0E20 phosamphaothai +!C1 U+0E21 momathai +!C2 U+0E22 yoyakthai +!C3 U+0E23 roruathai +!C4 U+0E24 ruthai +!C5 U+0E25 lolingthai +!C6 U+0E26 luthai +!C7 U+0E27 wowaenthai +!C8 U+0E28 sosalathai +!C9 U+0E29 sorusithai +!CA U+0E2A sosuathai +!CB U+0E2B hohipthai +!CC U+0E2C lochulathai +!CD U+0E2D oangthai +!CE U+0E2E honokhukthai +!CF U+0E2F paiyannoithai +!D0 U+0E30 saraathai +!D1 U+0E31 maihanakatthai +!D2 U+0E32 saraaathai +!D3 U+0E33 saraamthai +!D4 U+0E34 saraithai +!D5 U+0E35 saraiithai +!D6 U+0E36 sarauethai +!D7 U+0E37 saraueethai +!D8 U+0E38 sarauthai +!D9 U+0E39 sarauuthai +!DA U+0E3A phinthuthai +!DF U+0E3F bahtthai +!E0 U+0E40 saraethai +!E1 U+0E41 saraaethai +!E2 U+0E42 saraothai +!E3 U+0E43 saraaimaimuanthai +!E4 U+0E44 saraaimaimalaithai +!E5 U+0E45 lakkhangyaothai +!E6 U+0E46 maiyamokthai +!E7 U+0E47 maitaikhuthai +!E8 U+0E48 maiekthai +!E9 U+0E49 maithothai +!EA U+0E4A maitrithai +!EB U+0E4B maichattawathai +!EC U+0E4C thanthakhatthai +!ED U+0E4D nikhahitthai +!EE U+0E4E yamakkanthai +!EF U+0E4F fongmanthai +!F0 U+0E50 zerothai +!F1 U+0E51 onethai +!F2 U+0E52 twothai +!F3 U+0E53 threethai +!F4 U+0E54 fourthai +!F5 U+0E55 fivethai +!F6 U+0E56 sixthai +!F7 U+0E57 seventhai +!F8 U+0E58 eightthai +!F9 U+0E59 ninethai +!FA U+0E5A angkhankhuthai +!FB U+0E5B khomutthai diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-15.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-15.map new file mode 100644 index 000000000..2689703d1 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-15.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+20AC Euro +!A5 U+00A5 yen +!A6 U+0160 Scaron +!A7 U+00A7 section +!A8 U+0161 scaron +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+017D Zcaron +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+017E zcaron +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+0152 OE +!BD U+0153 oe +!BE U+0178 Ydieresis +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-16.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-16.map new file mode 100644 index 000000000..89b802a7a --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-16.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+0105 aogonek +!A3 U+0141 Lslash +!A4 U+20AC Euro +!A5 U+201E quotedblbase +!A6 U+0160 Scaron +!A7 U+00A7 section +!A8 U+0161 scaron +!A9 U+00A9 copyright +!AA U+0218 Scommaaccent +!AB U+00AB guillemotleft +!AC U+0179 Zacute +!AD U+00AD hyphen +!AE U+017A zacute +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+010C Ccaron +!B3 U+0142 lslash +!B4 U+017D Zcaron +!B5 U+201D quotedblright +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+017E zcaron +!B9 U+010D ccaron +!BA U+0219 scommaaccent +!BB U+00BB guillemotright +!BC U+0152 OE +!BD U+0153 oe +!BE U+0178 Ydieresis +!BF U+017C zdotaccent +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0106 Cacute +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+015A Sacute +!D8 U+0170 Uhungarumlaut +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0118 Eogonek +!DE U+021A Tcommaaccent +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+0107 cacute +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+015B sacute +!F8 U+0171 uhungarumlaut +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0119 eogonek +!FE U+021B tcommaaccent +!FF U+00FF ydieresis diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-2.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-2.map new file mode 100644 index 000000000..af9588d05 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-2.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+02D8 breve +!A3 U+0141 Lslash +!A4 U+00A4 currency +!A5 U+013D Lcaron +!A6 U+015A Sacute +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+0160 Scaron +!AA U+015E Scedilla +!AB U+0164 Tcaron +!AC U+0179 Zacute +!AD U+00AD hyphen +!AE U+017D Zcaron +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+0105 aogonek +!B2 U+02DB ogonek +!B3 U+0142 lslash +!B4 U+00B4 acute +!B5 U+013E lcaron +!B6 U+015B sacute +!B7 U+02C7 caron +!B8 U+00B8 cedilla +!B9 U+0161 scaron +!BA U+015F scedilla +!BB U+0165 tcaron +!BC U+017A zacute +!BD U+02DD hungarumlaut +!BE U+017E zcaron +!BF U+017C zdotaccent +!C0 U+0154 Racute +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0139 Lacute +!C6 U+0106 Cacute +!C7 U+00C7 Ccedilla +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+011A Ecaron +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+010E Dcaron +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+0147 Ncaron +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0158 Rcaron +!D9 U+016E Uring +!DA U+00DA Uacute +!DB U+0170 Uhungarumlaut +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+0162 Tcommaaccent +!DF U+00DF germandbls +!E0 U+0155 racute +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+013A lacute +!E6 U+0107 cacute +!E7 U+00E7 ccedilla +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+011B ecaron +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+010F dcaron +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+0148 ncaron +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0159 rcaron +!F9 U+016F uring +!FA U+00FA uacute +!FB U+0171 uhungarumlaut +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+0163 tcommaaccent +!FF U+02D9 dotaccent diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-4.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-4.map new file mode 100644 index 000000000..e81dd7f31 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-4.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+0138 kgreenlandic +!A3 U+0156 Rcommaaccent +!A4 U+00A4 currency +!A5 U+0128 Itilde +!A6 U+013B Lcommaaccent +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+0160 Scaron +!AA U+0112 Emacron +!AB U+0122 Gcommaaccent +!AC U+0166 Tbar +!AD U+00AD hyphen +!AE U+017D Zcaron +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+0105 aogonek +!B2 U+02DB ogonek +!B3 U+0157 rcommaaccent +!B4 U+00B4 acute +!B5 U+0129 itilde +!B6 U+013C lcommaaccent +!B7 U+02C7 caron +!B8 U+00B8 cedilla +!B9 U+0161 scaron +!BA U+0113 emacron +!BB U+0123 gcommaaccent +!BC U+0167 tbar +!BD U+014A Eng +!BE U+017E zcaron +!BF U+014B eng +!C0 U+0100 Amacron +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+012E Iogonek +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+0116 Edotaccent +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+012A Imacron +!D0 U+0110 Dcroat +!D1 U+0145 Ncommaaccent +!D2 U+014C Omacron +!D3 U+0136 Kcommaaccent +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+0172 Uogonek +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0168 Utilde +!DE U+016A Umacron +!DF U+00DF germandbls +!E0 U+0101 amacron +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+012F iogonek +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+0117 edotaccent +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+012B imacron +!F0 U+0111 dcroat +!F1 U+0146 ncommaaccent +!F2 U+014D omacron +!F3 U+0137 kcommaaccent +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+0173 uogonek +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0169 utilde +!FE U+016B umacron +!FF U+02D9 dotaccent diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-5.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-5.map new file mode 100644 index 000000000..8030fd544 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-5.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0401 afii10023 +!A2 U+0402 afii10051 +!A3 U+0403 afii10052 +!A4 U+0404 afii10053 +!A5 U+0405 afii10054 +!A6 U+0406 afii10055 +!A7 U+0407 afii10056 +!A8 U+0408 afii10057 +!A9 U+0409 afii10058 +!AA U+040A afii10059 +!AB U+040B afii10060 +!AC U+040C afii10061 +!AD U+00AD hyphen +!AE U+040E afii10062 +!AF U+040F afii10145 +!B0 U+0410 afii10017 +!B1 U+0411 afii10018 +!B2 U+0412 afii10019 +!B3 U+0413 afii10020 +!B4 U+0414 afii10021 +!B5 U+0415 afii10022 +!B6 U+0416 afii10024 +!B7 U+0417 afii10025 +!B8 U+0418 afii10026 +!B9 U+0419 afii10027 +!BA U+041A afii10028 +!BB U+041B afii10029 +!BC U+041C afii10030 +!BD U+041D afii10031 +!BE U+041E afii10032 +!BF U+041F afii10033 +!C0 U+0420 afii10034 +!C1 U+0421 afii10035 +!C2 U+0422 afii10036 +!C3 U+0423 afii10037 +!C4 U+0424 afii10038 +!C5 U+0425 afii10039 +!C6 U+0426 afii10040 +!C7 U+0427 afii10041 +!C8 U+0428 afii10042 +!C9 U+0429 afii10043 +!CA U+042A afii10044 +!CB U+042B afii10045 +!CC U+042C afii10046 +!CD U+042D afii10047 +!CE U+042E afii10048 +!CF U+042F afii10049 +!D0 U+0430 afii10065 +!D1 U+0431 afii10066 +!D2 U+0432 afii10067 +!D3 U+0433 afii10068 +!D4 U+0434 afii10069 +!D5 U+0435 afii10070 +!D6 U+0436 afii10072 +!D7 U+0437 afii10073 +!D8 U+0438 afii10074 +!D9 U+0439 afii10075 +!DA U+043A afii10076 +!DB U+043B afii10077 +!DC U+043C afii10078 +!DD U+043D afii10079 +!DE U+043E afii10080 +!DF U+043F afii10081 +!E0 U+0440 afii10082 +!E1 U+0441 afii10083 +!E2 U+0442 afii10084 +!E3 U+0443 afii10085 +!E4 U+0444 afii10086 +!E5 U+0445 afii10087 +!E6 U+0446 afii10088 +!E7 U+0447 afii10089 +!E8 U+0448 afii10090 +!E9 U+0449 afii10091 +!EA U+044A afii10092 +!EB U+044B afii10093 +!EC U+044C afii10094 +!ED U+044D afii10095 +!EE U+044E afii10096 +!EF U+044F afii10097 +!F0 U+2116 afii61352 +!F1 U+0451 afii10071 +!F2 U+0452 afii10099 +!F3 U+0453 afii10100 +!F4 U+0454 afii10101 +!F5 U+0455 afii10102 +!F6 U+0456 afii10103 +!F7 U+0457 afii10104 +!F8 U+0458 afii10105 +!F9 U+0459 afii10106 +!FA U+045A afii10107 +!FB U+045B afii10108 +!FC U+045C afii10109 +!FD U+00A7 section +!FE U+045E afii10110 +!FF U+045F afii10193 diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-7.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-7.map new file mode 100644 index 000000000..be8698afc --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-7.map @@ -0,0 +1,250 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+2018 quoteleft +!A2 U+2019 quoteright +!A3 U+00A3 sterling +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AF U+2015 afii00208 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+0384 tonos +!B5 U+0385 dieresistonos +!B6 U+0386 Alphatonos +!B7 U+00B7 periodcentered +!B8 U+0388 Epsilontonos +!B9 U+0389 Etatonos +!BA U+038A Iotatonos +!BB U+00BB guillemotright +!BC U+038C Omicrontonos +!BD U+00BD onehalf +!BE U+038E Upsilontonos +!BF U+038F Omegatonos +!C0 U+0390 iotadieresistonos +!C1 U+0391 Alpha +!C2 U+0392 Beta +!C3 U+0393 Gamma +!C4 U+0394 Delta +!C5 U+0395 Epsilon +!C6 U+0396 Zeta +!C7 U+0397 Eta +!C8 U+0398 Theta +!C9 U+0399 Iota +!CA U+039A Kappa +!CB U+039B Lambda +!CC U+039C Mu +!CD U+039D Nu +!CE U+039E Xi +!CF U+039F Omicron +!D0 U+03A0 Pi +!D1 U+03A1 Rho +!D3 U+03A3 Sigma +!D4 U+03A4 Tau +!D5 U+03A5 Upsilon +!D6 U+03A6 Phi +!D7 U+03A7 Chi +!D8 U+03A8 Psi +!D9 U+03A9 Omega +!DA U+03AA Iotadieresis +!DB U+03AB Upsilondieresis +!DC U+03AC alphatonos +!DD U+03AD epsilontonos +!DE U+03AE etatonos +!DF U+03AF iotatonos +!E0 U+03B0 upsilondieresistonos +!E1 U+03B1 alpha +!E2 U+03B2 beta +!E3 U+03B3 gamma +!E4 U+03B4 delta +!E5 U+03B5 epsilon +!E6 U+03B6 zeta +!E7 U+03B7 eta +!E8 U+03B8 theta +!E9 U+03B9 iota +!EA U+03BA kappa +!EB U+03BB lambda +!EC U+03BC mu +!ED U+03BD nu +!EE U+03BE xi +!EF U+03BF omicron +!F0 U+03C0 pi +!F1 U+03C1 rho +!F2 U+03C2 sigma1 +!F3 U+03C3 sigma +!F4 U+03C4 tau +!F5 U+03C5 upsilon +!F6 U+03C6 phi +!F7 U+03C7 chi +!F8 U+03C8 psi +!F9 U+03C9 omega +!FA U+03CA iotadieresis +!FB U+03CB upsilondieresis +!FC U+03CC omicrontonos +!FD U+03CD upsilontonos +!FE U+03CE omegatonos diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-9.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-9.map new file mode 100644 index 000000000..a60bb19d4 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/iso-8859-9.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+011E Gbreve +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0130 Idotaccent +!DE U+015E Scedilla +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+011F gbreve +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0131 dotlessi +!FE U+015F scedilla +!FF U+00FF ydieresis diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-r.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-r.map new file mode 100644 index 000000000..026880d66 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-r.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+2500 SF100000 +!81 U+2502 SF110000 +!82 U+250C SF010000 +!83 U+2510 SF030000 +!84 U+2514 SF020000 +!85 U+2518 SF040000 +!86 U+251C SF080000 +!87 U+2524 SF090000 +!88 U+252C SF060000 +!89 U+2534 SF070000 +!8A U+253C SF050000 +!8B U+2580 upblock +!8C U+2584 dnblock +!8D U+2588 block +!8E U+258C lfblock +!8F U+2590 rtblock +!90 U+2591 ltshade +!91 U+2592 shade +!92 U+2593 dkshade +!93 U+2320 integraltp +!94 U+25A0 filledbox +!95 U+2219 periodcentered +!96 U+221A radical +!97 U+2248 approxequal +!98 U+2264 lessequal +!99 U+2265 greaterequal +!9A U+00A0 space +!9B U+2321 integralbt +!9C U+00B0 degree +!9D U+00B2 twosuperior +!9E U+00B7 periodcentered +!9F U+00F7 divide +!A0 U+2550 SF430000 +!A1 U+2551 SF240000 +!A2 U+2552 SF510000 +!A3 U+0451 afii10071 +!A4 U+2553 SF520000 +!A5 U+2554 SF390000 +!A6 U+2555 SF220000 +!A7 U+2556 SF210000 +!A8 U+2557 SF250000 +!A9 U+2558 SF500000 +!AA U+2559 SF490000 +!AB U+255A SF380000 +!AC U+255B SF280000 +!AD U+255C SF270000 +!AE U+255D SF260000 +!AF U+255E SF360000 +!B0 U+255F SF370000 +!B1 U+2560 SF420000 +!B2 U+2561 SF190000 +!B3 U+0401 afii10023 +!B4 U+2562 SF200000 +!B5 U+2563 SF230000 +!B6 U+2564 SF470000 +!B7 U+2565 SF480000 +!B8 U+2566 SF410000 +!B9 U+2567 SF450000 +!BA U+2568 SF460000 +!BB U+2569 SF400000 +!BC U+256A SF540000 +!BD U+256B SF530000 +!BE U+256C SF440000 +!BF U+00A9 copyright +!C0 U+044E afii10096 +!C1 U+0430 afii10065 +!C2 U+0431 afii10066 +!C3 U+0446 afii10088 +!C4 U+0434 afii10069 +!C5 U+0435 afii10070 +!C6 U+0444 afii10086 +!C7 U+0433 afii10068 +!C8 U+0445 afii10087 +!C9 U+0438 afii10074 +!CA U+0439 afii10075 +!CB U+043A afii10076 +!CC U+043B afii10077 +!CD U+043C afii10078 +!CE U+043D afii10079 +!CF U+043E afii10080 +!D0 U+043F afii10081 +!D1 U+044F afii10097 +!D2 U+0440 afii10082 +!D3 U+0441 afii10083 +!D4 U+0442 afii10084 +!D5 U+0443 afii10085 +!D6 U+0436 afii10072 +!D7 U+0432 afii10067 +!D8 U+044C afii10094 +!D9 U+044B afii10093 +!DA U+0437 afii10073 +!DB U+0448 afii10090 +!DC U+044D afii10095 +!DD U+0449 afii10091 +!DE U+0447 afii10089 +!DF U+044A afii10092 +!E0 U+042E afii10048 +!E1 U+0410 afii10017 +!E2 U+0411 afii10018 +!E3 U+0426 afii10040 +!E4 U+0414 afii10021 +!E5 U+0415 afii10022 +!E6 U+0424 afii10038 +!E7 U+0413 afii10020 +!E8 U+0425 afii10039 +!E9 U+0418 afii10026 +!EA U+0419 afii10027 +!EB U+041A afii10028 +!EC U+041B afii10029 +!ED U+041C afii10030 +!EE U+041D afii10031 +!EF U+041E afii10032 +!F0 U+041F afii10033 +!F1 U+042F afii10049 +!F2 U+0420 afii10034 +!F3 U+0421 afii10035 +!F4 U+0422 afii10036 +!F5 U+0423 afii10037 +!F6 U+0416 afii10024 +!F7 U+0412 afii10019 +!F8 U+042C afii10046 +!F9 U+042B afii10045 +!FA U+0417 afii10025 +!FB U+0428 afii10042 +!FC U+042D afii10047 +!FD U+0429 afii10043 +!FE U+0427 afii10041 +!FF U+042A afii10044 diff --git a/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-u.map b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-u.map new file mode 100644 index 000000000..97d9d031b --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/maps/koi8-u.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+2500 SF100000 +!81 U+2502 SF110000 +!82 U+250C SF010000 +!83 U+2510 SF030000 +!84 U+2514 SF020000 +!85 U+2518 SF040000 +!86 U+251C SF080000 +!87 U+2524 SF090000 +!88 U+252C SF060000 +!89 U+2534 SF070000 +!8A U+253C SF050000 +!8B U+2580 upblock +!8C U+2584 dnblock +!8D U+2588 block +!8E U+258C lfblock +!8F U+2590 rtblock +!90 U+2591 ltshade +!91 U+2592 shade +!92 U+2593 dkshade +!93 U+2320 integraltp +!94 U+25A0 filledbox +!95 U+2022 bullet +!96 U+221A radical +!97 U+2248 approxequal +!98 U+2264 lessequal +!99 U+2265 greaterequal +!9A U+00A0 space +!9B U+2321 integralbt +!9C U+00B0 degree +!9D U+00B2 twosuperior +!9E U+00B7 periodcentered +!9F U+00F7 divide +!A0 U+2550 SF430000 +!A1 U+2551 SF240000 +!A2 U+2552 SF510000 +!A3 U+0451 afii10071 +!A4 U+0454 afii10101 +!A5 U+2554 SF390000 +!A6 U+0456 afii10103 +!A7 U+0457 afii10104 +!A8 U+2557 SF250000 +!A9 U+2558 SF500000 +!AA U+2559 SF490000 +!AB U+255A SF380000 +!AC U+255B SF280000 +!AD U+0491 afii10098 +!AE U+255D SF260000 +!AF U+255E SF360000 +!B0 U+255F SF370000 +!B1 U+2560 SF420000 +!B2 U+2561 SF190000 +!B3 U+0401 afii10023 +!B4 U+0404 afii10053 +!B5 U+2563 SF230000 +!B6 U+0406 afii10055 +!B7 U+0407 afii10056 +!B8 U+2566 SF410000 +!B9 U+2567 SF450000 +!BA U+2568 SF460000 +!BB U+2569 SF400000 +!BC U+256A SF540000 +!BD U+0490 afii10050 +!BE U+256C SF440000 +!BF U+00A9 copyright +!C0 U+044E afii10096 +!C1 U+0430 afii10065 +!C2 U+0431 afii10066 +!C3 U+0446 afii10088 +!C4 U+0434 afii10069 +!C5 U+0435 afii10070 +!C6 U+0444 afii10086 +!C7 U+0433 afii10068 +!C8 U+0445 afii10087 +!C9 U+0438 afii10074 +!CA U+0439 afii10075 +!CB U+043A afii10076 +!CC U+043B afii10077 +!CD U+043C afii10078 +!CE U+043D afii10079 +!CF U+043E afii10080 +!D0 U+043F afii10081 +!D1 U+044F afii10097 +!D2 U+0440 afii10082 +!D3 U+0441 afii10083 +!D4 U+0442 afii10084 +!D5 U+0443 afii10085 +!D6 U+0436 afii10072 +!D7 U+0432 afii10067 +!D8 U+044C afii10094 +!D9 U+044B afii10093 +!DA U+0437 afii10073 +!DB U+0448 afii10090 +!DC U+044D afii10095 +!DD U+0449 afii10091 +!DE U+0447 afii10089 +!DF U+044A afii10092 +!E0 U+042E afii10048 +!E1 U+0410 afii10017 +!E2 U+0411 afii10018 +!E3 U+0426 afii10040 +!E4 U+0414 afii10021 +!E5 U+0415 afii10022 +!E6 U+0424 afii10038 +!E7 U+0413 afii10020 +!E8 U+0425 afii10039 +!E9 U+0418 afii10026 +!EA U+0419 afii10027 +!EB U+041A afii10028 +!EC U+041B afii10029 +!ED U+041C afii10030 +!EE U+041D afii10031 +!EF U+041E afii10032 +!F0 U+041F afii10033 +!F1 U+042F afii10049 +!F2 U+0420 afii10034 +!F3 U+0421 afii10035 +!F4 U+0422 afii10036 +!F5 U+0423 afii10037 +!F6 U+0416 afii10024 +!F7 U+0412 afii10019 +!F8 U+042C afii10046 +!F9 U+042B afii10045 +!FA U+0417 afii10025 +!FB U+0428 afii10042 +!FC U+042D afii10047 +!FD U+0429 afii10043 +!FE U+0427 afii10041 +!FF U+042A afii10044 diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/AdobeFontMetrics.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php similarity index 98% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/AdobeFontMetrics.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php index a62daaa99..e75385f51 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/AdobeFontMetrics.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/AdobeFontMetrics.php @@ -35,7 +35,7 @@ class AdobeFontMetrics { $encoding = preg_replace("/[^a-z0-9-_]/", "", $encoding); $map_file = dirname(__FILE__) . "/../maps/$encoding.map"; if (!file_exists($map_file)) { - throw new \Exception("Unkown encoding ($encoding)"); + throw new \Exception("Unknown encoding ($encoding)"); } $map = new EncodingMap($map_file); @@ -139,7 +139,7 @@ class AdobeFontMetrics { $this->endSection("CharMetrics"); $kern = $font->getData("kern", "subtable"); - $tree = $kern["tree"]; + $tree = is_array($kern) ? $kern["tree"] : null; if (!$encoding && is_array($tree)) { $this->startSection("KernData"); diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Autoloader.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Autoloader.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Autoloader.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Autoloader.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/BinaryStream.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/BinaryStream.php similarity index 97% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/BinaryStream.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/BinaryStream.php index ab1045446..c7eb52f84 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/BinaryStream.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/BinaryStream.php @@ -63,7 +63,7 @@ class BinaryStream { */ public function open($filename, $mode = self::modeRead) { if (!in_array($mode, array(self::modeRead, self::modeWrite, self::modeReadWrite))) { - throw new \Exception("Unkown file open mode"); + throw new \Exception("Unknown file open mode"); } $this->f = fopen($filename, $mode); @@ -137,12 +137,17 @@ class BinaryStream { fseek($this->f, $n, SEEK_CUR); } + /** + * @param int $n The number of bytes to read + * + * @return string + */ public function read($n) { if ($n < 1) { return ""; } - return fread($this->f, $n); + return (string) fread($this->f, $n); } public function write($data, $length = null) { @@ -282,7 +287,7 @@ class BinaryStream { $date = 0; } - return strftime("%Y-%m-%d %H:%M:%S", $date); + return date("Y-m-d H:i:s", $date); } public function writeLongDateTime($data) { diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/File.php similarity index 93% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/File.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/File.php index 13d592572..f51d876c5 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/File.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/File.php @@ -61,22 +61,21 @@ class File extends \FontLib\TrueType\File { // TODO Read font data ... } - /** - * Little endian version of the read method - * - * @param int $n The number of bytes to read - * - * @return string - */ + /** + * Little endian version of the read method + * + * @param int $n The number of bytes to read + * + * @return string + */ public function read($n) { if ($n < 1) { return ""; } - $string = fread($this->f, $n); - $chunks = str_split($string, 2); + $string = (string) fread($this->f, $n); + $chunks = mb_str_split($string, 2, '8bit'); $chunks = array_map("strrev", $chunks); - return implode("", $chunks); } diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/Header.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/EOT/Header.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EOT/Header.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/EncodingMap.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EncodingMap.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/EncodingMap.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/EncodingMap.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Exception/FontNotFoundException.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Exception/FontNotFoundException.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Exception/FontNotFoundException.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Exception/FontNotFoundException.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Font.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Font.php similarity index 96% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Font.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Font.php index ecc216e81..e13a65332 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Font.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Font.php @@ -28,7 +28,7 @@ class Font { throw new FontNotFoundException($file); } - $header = file_get_contents($file, false, null, null, 4); + $header = file_get_contents($file, false, null, 0, 4); $class = null; switch ($header) { diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/Outline.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/Outline.php similarity index 93% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/Outline.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/Outline.php index 330db0918..639ff60aa 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/Outline.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/Outline.php @@ -33,6 +33,9 @@ class Outline extends BinaryStream { public $xMax; public $yMax; + /** + * @var string|null + */ public $raw; /** @@ -75,11 +78,7 @@ class Outline extends BinaryStream { function parse(BinaryStream $font) { $font->seek($this->offset); - if (!$this->size) { - return; - } - - $this->raw = $font->read($this->size); + $this->raw = $font->read($this->size); } function parseData() { @@ -96,7 +95,7 @@ class Outline extends BinaryStream { function encode() { $font = $this->getFont(); - return $font->write($this->raw, strlen($this->raw)); + return $font->write($this->raw, mb_strlen((string) $this->raw, '8bit')); } function getSVGContours() { diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComponent.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComponent.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComponent.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComponent.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComposite.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComposite.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineComposite.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineComposite.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineSimple.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineSimple.php similarity index 99% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineSimple.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineSimple.php index 3c023de9a..56b2fb496 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Glyph/OutlineSimple.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Glyph/OutlineSimple.php @@ -265,7 +265,7 @@ class OutlineSimple extends Outline { $points = $this->points; } - $length = count($points); + $length = (empty($points) ? 0 : count($points)); $firstIndex = 0; $count = 0; @@ -332,4 +332,4 @@ class OutlineSimple extends Outline { function midValue($a, $b) { return $a + ($b - $a) / 2; } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Header.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Header.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Header.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/File.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/File.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/File.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/OpenType/TableDirectoryEntry.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/DirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/DirectoryEntry.php similarity index 94% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/DirectoryEntry.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/DirectoryEntry.php index 2b5846d3d..54a67af30 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/DirectoryEntry.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/DirectoryEntry.php @@ -36,15 +36,20 @@ class DirectoryEntry extends BinaryStream { protected $origF; + /** + * @param string $data + * + * @return int + */ static function computeChecksum($data) { - $len = strlen($data); + $len = mb_strlen($data, '8bit'); $mod = $len % 4; if ($mod) { $data = str_pad($data, $len + (4 - $mod), "\0"); } - $len = strlen($data); + $len = mb_strlen($data, '8bit'); $hi = 0x0000; $lo = 0x0000; diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Table.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Table.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Table.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Table.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/cmap.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/cmap.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/cmap.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/cmap.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/glyf.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/glyf.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/glyf.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/glyf.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/head.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/head.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/head.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/head.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hhea.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hhea.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hhea.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hhea.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hmtx.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hmtx.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/hmtx.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/hmtx.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/kern.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/kern.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/kern.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/kern.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/loca.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/loca.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/loca.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/loca.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/maxp.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/maxp.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/maxp.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/maxp.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/name.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/name.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/name.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/name.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/nameRecord.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/nameRecord.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/nameRecord.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/nameRecord.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/os2.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/os2.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/os2.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/os2.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/post.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/post.php similarity index 95% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/post.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/post.php index ec5806b78..030a942e6 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/Table/Type/post.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/Table/Type/post.php @@ -57,7 +57,9 @@ class post extends Table { $names[$g] = File::$macCharNames[$index]; } else { - $names[$g] = $namesPascal[$index - 258]; + if (array_key_exists($index - 258, $namesPascal)) { + $names[$g] = $namesPascal[$index - 258]; + } } } diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Collection.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Collection.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Collection.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Collection.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php similarity index 99% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/File.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php index b61da0f2e..3594479aa 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/File.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/File.php @@ -124,7 +124,7 @@ class File extends BinaryStream { } function utf8toUnicode($str) { - $len = strlen($str); + $len = mb_strlen($str, '8bit'); $out = array(); for ($i = 0; $i < $len; $i++) { diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Header.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/Header.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/Header.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/TrueType/TableDirectoryEntry.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/File.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/File.php similarity index 95% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/File.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/File.php index 9e54b3fb0..4668c2391 100644 --- a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/File.php +++ b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/File.php @@ -46,11 +46,11 @@ class File extends \FontLib\TrueType\File { $data = $this->read($entry->length); if ($entry->length < $entry->origLength) { - $data = gzuncompress($data); + $data = (string) gzuncompress($data); } // Prepare data ... - $length = strlen($data); + $length = mb_strlen($data, '8bit'); $entry->length = $entry->origLength = $length; $entry->offset = $dataOffset; diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/Header.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/Header.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/Header.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/Header.php diff --git a/library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php b/library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php similarity index 100% rename from library/vendor/dompdf/lib/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php rename to library/vendor/dompdf/vendor/phenx/php-font-lib/src/FontLib/WOFF/TableDirectoryEntry.php diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/LICENSE b/library/vendor/dompdf/vendor/phenx/php-svg-lib/LICENSE new file mode 100644 index 000000000..0a041280b --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/README.md b/library/vendor/dompdf/vendor/phenx/php-svg-lib/README.md new file mode 100644 index 000000000..2b8e6f67e --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/README.md @@ -0,0 +1,13 @@ +# SVG file parsing / rendering library + +[![Build Status](https://github.com/phenx/php-svg-lib/workflows/test/badge.svg)](https://github.com/phenx/php-svg-lib/actions) + + +[![Latest Stable Version](https://poser.pugx.org/phenx/php-svg-lib/v/stable)](https://packagist.org/packages/phenx/php-svg-lib) +[![Total Downloads](https://poser.pugx.org/phenx/php-svg-lib/downloads)](https://packagist.org/packages/phenx/php-svg-lib) +[![Latest Unstable Version](https://poser.pugx.org/phenx/php-svg-lib/v/unstable)](https://packagist.org/packages/phenx/php-svg-lib) +[![License](https://poser.pugx.org/phenx/php-svg-lib/license)](https://packagist.org/packages/phenx/php-svg-lib) + +The main purpose of this lib is to rasterize SVG to a surface which can be an image or a PDF for example, through a `\Svg\Surface` PHP interface. + +This project was initialized by the need to render SVG documents inside PDF files for the [DomPdf](http://dompdf.github.io) project. diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/composer.json b/library/vendor/dompdf/vendor/phenx/php-svg-lib/composer.json new file mode 100644 index 000000000..a6ed9c541 --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/composer.json @@ -0,0 +1,31 @@ +{ + "name": "phenx/php-svg-lib", + "type": "library", + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/PhenX/php-svg-lib", + "license": "LGPL-3.0", + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "autoload-dev": { + "psr-4": { + "Svg\\Tests\\": "tests/Svg" + } + }, + "require": { + "php": "^7.1 || ^8.0", + "ext-mbstring": "*", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + } +} diff --git a/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/CssLength.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/CssLength.php new file mode 100644 index 000000000..88eda8c6f --- /dev/null +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/CssLength.php @@ -0,0 +1,135 @@ + + */ + protected static $inchDivisions = [ + 'in' => 1, + 'cm' => 2.54, + 'mm' => 25.4, + 'q' => 101.6, + 'pc' => 6, + 'pt' => 72, + ]; + + /** + * The CSS length unit indicator. + * Will be lower-case and one of the units listed in the '$units' array or empty. + * + * @var string + */ + protected $unit = ''; + + /** + * The numeric value of the given length. + * + * @var float + */ + protected $value = 0; + + /** + * The original unparsed length provided. + * + * @var string + */ + protected $unparsed; + + public function __construct(string $length) + { + $this->unparsed = $length; + $this->parseLengthComponents($length); + } + + /** + * Parse out the unit and value components from the given string length. + */ + protected function parseLengthComponents(string $length): void + { + $length = strtolower($length); + + foreach (self::$units as $unit) { + $pos = strpos($length, $unit); + if ($pos) { + $this->value = floatval(substr($length, 0, $pos)); + $this->unit = $unit; + return; + } + } + + $this->unit = ''; + $this->value = floatval($length); + } + + /** + * Get the unit type of this css length. + * Units are standardised to be lower-cased. + * + * @return string + */ + public function getUnit(): string + { + return $this->unit; + } + + /** + * Get this CSS length in the equivalent pixel count size. + * + * @param float $referenceSize + * @param float $dpi + * + * @return float + */ + public function toPixels(float $referenceSize = 11.0, float $dpi = 96.0): float + { + // Standard relative units + if (in_array($this->unit, ['em', 'rem', 'ex', 'ch'])) { + return $this->value * $referenceSize; + } + + // Percentage relative units + if (in_array($this->unit, ['%', 'vw', 'vh', 'vmin', 'vmax'])) { + return $this->value * ($referenceSize / 100); + } + + // Inch relative units + if (in_array($this->unit, array_keys(static::$inchDivisions))) { + $inchValue = $this->value * $dpi; + $division = static::$inchDivisions[$this->unit]; + return $inchValue / $division; + } + + return $this->value; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/DefaultStyle.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/DefaultStyle.php similarity index 82% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/DefaultStyle.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/DefaultStyle.php index c0535c726..4e73d2948 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/DefaultStyle.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/DefaultStyle.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -10,11 +10,11 @@ namespace Svg; class DefaultStyle extends Style { - public $color = ''; + public $color = [0, 0, 0, 1]; public $opacity = 1.0; public $display = 'inline'; - public $fill = 'black'; + public $fill = [0, 0, 0, 1]; public $fillOpacity = 1.0; public $fillRule = 'nonzero'; @@ -26,4 +26,4 @@ class DefaultStyle extends Style public $strokeWidth = 1.0; public $strokeDasharray = 0; public $strokeDashoffset = 0; -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Document.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Document.php similarity index 97% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Document.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Document.php index 4ecdc761a..4de226e70 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Document.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Document.php @@ -41,9 +41,6 @@ class Document extends AbstractTag protected $pathBBox; protected $viewBox; - /** @var resource */ - protected $parser; - /** @var SurfaceInterface */ protected $surface; @@ -74,7 +71,7 @@ class Document extends AbstractTag array($this, "_charData") ); - return $this->parser = $parser; + return $parser; } public function __construct() { @@ -104,6 +101,11 @@ class Document extends AbstractTag return $this->height; } + public function getDiagonal() + { + return sqrt(($this->width)**2 + ($this->height)**2) / sqrt(2); + } + public function getDimensions() { $rootAttributes = null; @@ -138,12 +140,12 @@ class Document extends AbstractTag public function handleSizeAttributes($attributes){ if ($this->width === null) { if (isset($attributes["width"])) { - $width = Style::convertSize($attributes["width"], 400); + $width = $this->convertSize($attributes["width"], 400); $this->width = $width; } if (isset($attributes["height"])) { - $height = Style::convertSize($attributes["height"], 300); + $height = $this->convertSize($attributes["height"], 300); $this->height = $height; } diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Gradient/Stop.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Gradient/Stop.php similarity index 81% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Gradient/Stop.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Gradient/Stop.php index 14a36bdec..a37fb970b 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Gradient/Stop.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Gradient/Stop.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -13,4 +13,4 @@ class Stop public $offset; public $color; public $opacity = 1.0; -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Style.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Style.php similarity index 88% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Style.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Style.php index 9e7f6dba0..14b11e902 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Style.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Style.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien M�nager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -18,6 +18,8 @@ class Style const TYPE_ANGLE = 4; const TYPE_NUMBER = 5; + private $_parentStyle; + public $color; public $opacity; public $display; @@ -88,7 +90,7 @@ class Style $group = $tag->getParentGroup(); if ($group) { $parent_style = $group->getStyle(); - + $this->_parentStyle = $parent_style; foreach ($parent_style as $_key => $_value) { if ($_value !== null) { $this->$_key = $_value; @@ -145,13 +147,24 @@ class Style protected function fillStyles($styles) { - foreach ($this->getStyleMap() as $from => $spec) { + $style_map = $this->getStyleMap(); + foreach ($style_map as $from => $spec) { if (isset($styles[$from])) { list($to, $type) = $spec; $value = null; switch ($type) { case self::TYPE_COLOR: $value = self::parseColor($styles[$from]); + if ($value === "currentcolor") { + if ($type === "color") { + $value = $this->_parentStyle->color; + } else { + $value = $this->color; + } + } + if ($value !== null && $value[3] !== 1 && array_key_exists("{$from}-opacity", $style_map) === true) { + $styles["{$from}-opacity"] = $value[3]; + } break; case self::TYPE_NUMBER: @@ -177,8 +190,7 @@ class Style if (count($parts) == 2) { $color = $parts[1]; - } - else { + } else { $color = $parts[0]; } @@ -186,6 +198,10 @@ class Style return "none"; } + if ($color === "currentcolor") { + return "currentcolor"; + } + // SVG color name if (isset(self::$colorNames[$color])) { return self::parseHexColor(self::$colorNames[$color]); @@ -198,18 +214,18 @@ class Style // RGB color if (strpos($color, "rgb") !== false) { - return self::getTriplet($color); + return self::getQuad($color); } // RGB color if (strpos($color, "hsl") !== false) { - $triplet = self::getTriplet($color, true); + $quad = self::getQuad($color, true); - if ($triplet == null) { + if ($quad == null) { return null; } - list($h, $s, $l) = $triplet; + list($h, $s, $l, $a) = $quad; $r = $l; $g = $l; @@ -258,11 +274,13 @@ class Style break; } } + $a = $a * 255; return array( $r * 255.0, $g * 255.0, $b * 255.0, + $a ); } @@ -282,7 +300,7 @@ class Style return null; } - static function getTriplet($color, $percent = false) { + static function getQuad($color, $percent = false) { $i = strpos($color, "("); $j = strpos($color, ")"); @@ -291,46 +309,60 @@ class Style return null; } - $triplet = preg_split("/\\s*,\\s*/", trim(substr($color, $i + 1, $j - $i - 1))); + $quad = preg_split("/\\s*[,\\/]\\s*/", trim(substr($color, $i + 1, $j - $i - 1))); + if (!isset($quad[3])) { + $quad[3] = 1; + } - if (count($triplet) != 3) { + if (count($quad) != 3 && count($quad) != 4) { return null; } - foreach (array_keys($triplet) as $c) { - $triplet[$c] = trim($triplet[$c]); + foreach (array_keys($quad) as $c) { + $quad[$c] = trim($quad[$c]); if ($percent) { - if ($triplet[$c][strlen($triplet[$c]) - 1] === "%") { - $triplet[$c] = $triplet[$c] / 100; + if ($quad[$c][strlen($quad[$c]) - 1] === "%") { + $quad[$c] = floatval($quad[$c]) / 100; + } else { + $quad[$c] = $quad[$c] / 255; } - else { - $triplet[$c] = $triplet[$c] / 255; - } - } - else { - if ($triplet[$c][strlen($triplet[$c]) - 1] === "%") { - $triplet[$c] = round($triplet[$c] * 2.55); + } else { + if ($quad[$c][strlen($quad[$c]) - 1] === "%") { + $quad[$c] = round(floatval($quad[$c]) * 2.55); } } } - return $triplet; + return $quad; } static function parseHexColor($hex) { - $c = array(0, 0, 0); + $c = array(0, 0, 0, 1); // #FFFFFF if (isset($hex[6])) { $c[0] = hexdec(substr($hex, 1, 2)); $c[1] = hexdec(substr($hex, 3, 2)); $c[2] = hexdec(substr($hex, 5, 2)); + + if (isset($hex[7])) { + $alpha = substr($hex, 7, 2); + if (ctype_xdigit($alpha)) { + $c[3] = round(hexdec($alpha)/255, 2); + } + } } else { $c[0] = hexdec($hex[1] . $hex[1]); $c[1] = hexdec($hex[2] . $hex[2]); $c[2] = hexdec($hex[3] . $hex[3]); + + if (isset($hex[4])) { + if (ctype_xdigit($hex[4])) { + $c[3] = round(hexdec($hex[4] . $hex[4])/255, 2); + } + } } return $c; @@ -356,47 +388,6 @@ class Style return $styles; } - /** - * Convert a size to a float - * - * @param string $size SVG size - * @param float $dpi DPI - * @param float $referenceSize Reference size - * - * @return float|null - */ - static function convertSize($size, $referenceSize = 11.0, $dpi = 96.0) { - $size = trim(strtolower($size)); - - if (is_numeric($size)) { - return $size; - } - - if ($pos = strpos($size, "px")) { - return floatval(substr($size, 0, $pos)); - } - - if ($pos = strpos($size, "pt")) { - return floatval(substr($size, 0, $pos)); - } - - if ($pos = strpos($size, "cm")) { - return floatval(substr($size, 0, $pos)) * $dpi; - } - - if ($pos = strpos($size, "%")) { - return $referenceSize * substr($size, 0, $pos) / 100; - } - - if ($pos = strpos($size, "em")) { - return $referenceSize * substr($size, 0, $pos); - } - - // TODO cm, mm, pc, in, etc - - return null; - } - static $colorNames = array( 'antiquewhite' => '#FAEBD7', 'aqua' => '#00FFFF', @@ -547,4 +538,4 @@ class Style 'whitesmoke' => '#f5f5f5', 'yellowgreen' => '#9acd32', ); -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/Cpdf.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/CPdf.php similarity index 77% rename from library/vendor/dompdf/lib/Cpdf.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/CPdf.php index 7748d4f15..caa28a890 100644 --- a/library/vendor/dompdf/lib/Cpdf.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/CPdf.php @@ -15,11 +15,34 @@ * @license Public Domain http://creativecommons.org/licenses/publicdomain/ * @package Cpdf */ -use FontLib\Font; -use FontLib\BinaryStream; -class Cpdf +namespace Svg\Surface; + +class CPdf { + const PDF_VERSION = '1.7'; + + const ACROFORM_SIG_SIGNATURESEXISTS = 0x0001; + const ACROFORM_SIG_APPENDONLY = 0x0002; + + const ACROFORM_FIELD_BUTTON = 'Btn'; + const ACROFORM_FIELD_TEXT = 'Tx'; + const ACROFORM_FIELD_CHOICE = 'Ch'; + const ACROFORM_FIELD_SIG = 'Sig'; + + const ACROFORM_FIELD_READONLY = 0x0001; + const ACROFORM_FIELD_REQUIRED = 0x0002; + + const ACROFORM_FIELD_TEXT_MULTILINE = 0x1000; + const ACROFORM_FIELD_TEXT_PASSWORD = 0x2000; + const ACROFORM_FIELD_TEXT_RICHTEXT = 0x10000; + + const ACROFORM_FIELD_CHOICE_COMBO = 0x20000; + const ACROFORM_FIELD_CHOICE_EDIT = 0x40000; + const ACROFORM_FIELD_CHOICE_SORT = 0x80000; + const ACROFORM_FIELD_CHOICE_MULTISELECT = 0x200000; + + const XOBJECT_SUBTYPE_FORM = 'Form'; /** * @var integer The current number of pdf objects in the document @@ -29,18 +52,40 @@ class Cpdf /** * @var array This array contains all of the pdf objects, ready for final assembly */ - public $objects = array(); + public $objects = []; /** * @var integer The objectId (number within the objects array) of the document catalog */ public $catalogId; + /** + * @var integer The objectId (number within the objects array) of indirect references (Javascript EmbeddedFiles) + */ + protected $indirectReferenceId = 0; + + /** + * @var integer The objectId (number within the objects array) + */ + protected $embeddedFilesId = 0; + + /** + * AcroForm objectId + * + * @var integer + */ + public $acroFormId; + + /** + * @var int + */ + public $signatureMaxLen = 5000; + /** * @var array Array carrying information about the fonts that the system currently knows about * Used to ensure that a font is not loaded twice, among other things */ - public $fonts = array(); + public $fonts = []; /** * @var string The default font metrics file to use if no other font has been loaded. @@ -91,7 +136,7 @@ class Cpdf /** * @var array Number of graphic state resources used */ - private $gstates = array(); + private $gstates = []; /** * @var array Current color for fill operations, defaults to inactive value, @@ -117,18 +162,18 @@ class Cpdf /** * @var array Current line transparency (partial graphics state) */ - public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0); + public $currentLineTransparency = ["mode" => "Normal", "opacity" => 1.0]; /** * array Current fill transparency (partial graphics state) */ - public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0); + public $currentFillTransparency = ["mode" => "Normal", "opacity" => 1.0]; /** * @var array An array which is used to save the state of the document, mainly the colors and styles * it is used to temporarily change to another state, then change back to what it was before */ - public $stateStack = array(); + public $stateStack = []; /** * @var integer Number of elements within the state stack @@ -143,7 +188,7 @@ class Cpdf /** * @var array Object Id storage stack */ - public $stack = array(); + public $stack = []; /** * @var integer Number of elements within the object Id storage stack @@ -154,12 +199,12 @@ class Cpdf * an array which contains information about the objects which are not firmly attached to pages * these have been added with the addObject function */ - public $looseObjects = array(); + public $looseObjects = []; /** * array contains information about how the loose objects are to be added to the document */ - public $addLooseObjects = array(); + public $addLooseObjects = []; /** * @var integer The objectId of the information object for the document @@ -176,7 +221,7 @@ class Cpdf * @var array An array containing options about the document * it defaults to turning on the compression of the objects */ - public $options = array('compression' => true); + public $options = ['compression' => true]; /** * @var integer The objectId of the first page of the document @@ -193,7 +238,7 @@ class Cpdf * this used so that the code knows which font is the bold version of another font, etc. * the value of this array is initialised in the constructor function. */ - public $fontFamilies = array(); + public $fontFamilies = []; /** * @var string Folder for php serialized formats of font metrics files. @@ -255,7 +300,7 @@ class Cpdf /** * @var array Array which forms a stack to keep track of nested callback functions */ - public $callback = array(); + public $callback = []; /** * @var integer The number of callback functions in the callback array @@ -266,7 +311,7 @@ class Cpdf * @var array Store label->id pairs for named destinations, these will be used to replace internal links * done this way so that destinations can be defined after the location that links to them */ - public $destinations = array(); + public $destinations = []; /** * @var array Store the stack for the transaction commands, each item in here is a record of the values of all the @@ -279,7 +324,17 @@ class Cpdf * @var array Table of Image origin filenames and image labels which were already added with o_image(). * Allows to merge identical images */ - public $imagelist = array(); + public $imagelist = []; + + /** + * @var array Table of already added alpha and plain image files for transparent PNG images. + */ + protected $imageAlphaList = []; + + /** + * @var array List of temporary image files to be deleted after processing. + */ + protected $imageCache = []; /** * @var boolean Whether the text passed in should be treated as Unicode or just local character set. @@ -299,22 +354,27 @@ class Cpdf /** * @var array Current page size */ - protected $currentPageSize = array("width" => 0, "height" => 0); + protected $currentPageSize = ["width" => 0, "height" => 0]; /** * @var array All the chars that will be required in the font subsets */ - protected $stringSubsets = array(); + protected $stringSubsets = []; /** * @var string The target internal encoding */ - static protected $targetEncoding = 'Windows-1252'; + protected static $targetEncoding = 'Windows-1252'; + + /** + * @var array + */ + protected $byteRange = array(); /** * @var array The list of the core fonts */ - static protected $coreFonts = array( + protected static $coreFonts = [ 'courier', 'courier-bold', 'courier-oblique', @@ -329,7 +389,7 @@ class Cpdf 'times-bolditalic', 'symbol', 'zapfdingbats' - ); + ]; /** * Class constructor @@ -340,7 +400,7 @@ class Cpdf * @param string $fontcache The font cache folder * @param string $tmp The temporary folder */ - function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '') + function __construct($pageSize = [0, 0, 612, 792], $isUnicode = false, $fontcache = '', $tmp = '') { $this->isUnicode = $isUnicode; $this->fontcache = rtrim($fontcache, DIRECTORY_SEPARATOR."/\\"); @@ -357,6 +417,15 @@ class Cpdf $this->setFontFamily('init'); } + public function __destruct() + { + foreach ($this->imageCache as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + /** * Document object methods (internal use only) * @@ -384,7 +453,7 @@ class Cpdf { switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'destination', 'info' => array()); + $this->objects[$id] = ['t' => 'destination', 'info' => []]; $tmp = ''; switch ($options['type']) { case 'XYZ': @@ -429,7 +498,7 @@ class Cpdf { switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'viewerPreferences', 'info' => array()); + $this->objects[$id] = ['t' => 'viewerPreferences', 'info' => []]; break; case 'add': @@ -463,28 +532,28 @@ class Cpdf // Named with limited valid values case 'NonFullScreenPageMode': - if (!in_array($v, array('UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'))) { + if (!in_array($v, ['UseNone', 'UseOutlines', 'UseThumbs', 'UseOC'])) { break; } $o['info'][$k] = $v; break; case 'Direction': - if (!in_array($v, array('L2R', 'R2L'))) { + if (!in_array($v, ['L2R', 'R2L'])) { break; } $o['info'][$k] = $v; break; case 'PrintScaling': - if (!in_array($v, array('None', 'AppDefault'))) { + if (!in_array($v, ['None', 'AppDefault'])) { break; } $o['info'][$k] = $v; break; case 'Duplex': - if (!in_array($v, array('None', 'AppDefault'))) { + if (!in_array($v, ['None', 'Simplex', 'DuplexFlipShortEdge', 'DuplexFlipLongEdge'])) { break; } $o['info'][$k] = $v; @@ -518,7 +587,7 @@ class Cpdf } $res .= "\n/$k $v"; } - $res .= "\n>>\n"; + $res .= "\n>>\nendobj"; return $res; } @@ -542,14 +611,15 @@ class Cpdf switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'catalog', 'info' => array()); + $this->objects[$id] = ['t' => 'catalog', 'info' => []]; $this->catalogId = $id; break; + case 'acroform': case 'outlines': case 'pages': case 'openHere': - case 'javascript': + case 'names': $o['info'][$action] = $options; break; @@ -586,8 +656,12 @@ class Cpdf $res .= "\n/OpenAction $v 0 R"; break; - case 'javascript': - $res .= "\n/Names <>"; + case 'names': + $res .= "\n/Names $v 0 R"; + break; + + case 'acroform': + $res .= "\n/AcroForm $v 0 R"; break; } } @@ -616,7 +690,7 @@ class Cpdf switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'pages', 'info' => array()); + $this->objects[$id] = ['t' => 'pages', 'info' => []]; $this->o_catalog($this->catalogId, 'pages', $id); break; @@ -666,19 +740,19 @@ class Cpdf case 'mediaBox': $o['info']['mediaBox'] = $options; // which should be an array of 4 numbers - $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]); + $this->currentPageSize = ['width' => $options[2], 'height' => $options[3]]; break; case 'font': - $o['info']['fonts'][] = array('objNum' => $options['objNum'], 'fontNum' => $options['fontNum']); + $o['info']['fonts'][] = ['objNum' => $options['objNum'], 'fontNum' => $options['fontNum']]; break; case 'extGState': - $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']); + $o['info']['extGStates'][] = ['objNum' => $options['objNum'], 'stateNum' => $options['stateNum']]; break; case 'xObject': - $o['info']['xObjects'][] = array('objNum' => $options['objNum'], 'label' => $options['label']); + $o['info']['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']]; break; case 'out': @@ -764,7 +838,7 @@ class Cpdf switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'outlines', 'info' => array('outlines' => array())); + $this->objects[$id] = ['t' => 'outlines', 'info' => ['outlines' => []]]; $this->o_catalog($this->catalogId, 'outlines', $id); break; @@ -797,6 +871,7 @@ class Cpdf * @param $action * @param string|array $options * @return string|null + * @throws FontNotFoundException */ protected function o_font($id, $action, $options = '') { @@ -806,14 +881,15 @@ class Cpdf switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'font', - 'info' => array( + 'info' => [ 'name' => $options['name'], 'fontFileName' => $options['fontFileName'], - 'SubType' => 'Type1' - ) - ); + 'SubType' => 'Type1', + 'isSubsetting' => $options['isSubsetting'] + ] + ]; $fontNum = $this->numFonts; $this->objects[$id]['info']['fontNum'] = $fontNum; @@ -864,29 +940,33 @@ class Cpdf } // also tell the pages node about the new font - $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id)); + $this->o_pages($this->currentNode, 'font', ['fontNum' => $fontNum, 'objNum' => $id]); break; case 'add': - foreach ($options as $k => $v) { - switch ($k) { - case 'BaseFont': - $o['info']['name'] = $v; - break; - case 'FirstChar': - case 'LastChar': - case 'Widths': - case 'FontDescriptor': - case 'SubType': - $this->addMessage('o_font ' . $k . " : " . $v); - $o['info'][$k] = $v; - break; - } - } + $font_options = $this->processFont($id, $o['info']); - // pass values down to descendent font - if (isset($o['info']['cidFont'])) { - $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options); + if ($font_options !== false) { + foreach ($font_options as $k => $v) { + switch ($k) { + case 'BaseFont': + $o['info']['name'] = $v; + break; + case 'FirstChar': + case 'LastChar': + case 'Widths': + case 'FontDescriptor': + case 'SubType': + $this->addMessage('o_font ' . $k . " : " . $v); + $o['info'][$k] = $v; + break; + } + } + + // pass values down to descendent font + if (isset($o['info']['cidFont'])) { + $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $font_options); + } } break; @@ -951,6 +1031,237 @@ class Cpdf return null; } + protected function getFontSubsettingTag(array $font): string + { + // convert font num to hexavigesimal numeral system letters A - Z only + $base_26 = strtoupper(base_convert($font['fontNum'], 10, 26)); + for ($i = 0; $i < strlen($base_26); $i++) { + $char = $base_26[$i]; + if ($char <= "9") { + $base_26[$i] = chr(65 + intval($char)); + } else { + $base_26[$i] = chr(ord($char) + 10); + } + } + + return 'SUB' . str_pad($base_26, 3 , 'A', STR_PAD_LEFT); + } + + /** + * @param int $fontObjId + * @param array $object_info + * @return array|false + * @throws FontNotFoundException + */ + private function processFont(int $fontObjId, array $object_info) + { + $fontFileName = $object_info['fontFileName']; + if (!isset($this->fonts[$fontFileName])) { + return false; + } + + $font = &$this->fonts[$fontFileName]; + + $fileSuffix = $font['fileSuffix']; + $fileSuffixLower = strtolower($font['fileSuffix']); + $fbfile = "$fontFileName.$fileSuffix"; + $isTtfFont = $fileSuffixLower === 'ttf'; + $isPfbFont = $fileSuffixLower === 'pfb'; + + $this->addMessage('selectFont: checking for - ' . $fbfile); + + if (!$fileSuffix) { + $this->addMessage( + 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts' + ); + + return false; + } else { + $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName']; + // $fontObj = $this->numObj; + $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName"); + + // find the array of font widths, and put that into an object. + $firstChar = -1; + $lastChar = 0; + $widths = []; + $cid_widths = []; + + foreach ($font['C'] as $num => $d) { + if (intval($num) > 0 || $num == '0') { + if (!$font['isUnicode']) { + // With Unicode, widths array isn't used + if ($lastChar > 0 && $num > $lastChar + 1) { + for ($i = $lastChar + 1; $i < $num; $i++) { + $widths[] = 0; + } + } + } + + $widths[] = $d; + + if ($font['isUnicode']) { + $cid_widths[$num] = $d; + } + + if ($firstChar == -1) { + $firstChar = $num; + } + + $lastChar = $num; + } + } + + // also need to adjust the widths for the differences array + if (isset($object['differences'])) { + foreach ($object['differences'] as $charNum => $charName) { + if ($charNum > $lastChar) { + if (!$object['isUnicode']) { + // With Unicode, widths array isn't used + for ($i = $lastChar + 1; $i <= $charNum; $i++) { + $widths[] = 0; + } + } + + $lastChar = $charNum; + } + + if (isset($font['C'][$charName])) { + $widths[$charNum - $firstChar] = $font['C'][$charName]; + if ($font['isUnicode']) { + $cid_widths[$charName] = $font['C'][$charName]; + } + } + } + } + + if ($font['isUnicode']) { + $font['CIDWidths'] = $cid_widths; + } + + $this->addMessage('selectFont: FirstChar = ' . $firstChar); + $this->addMessage('selectFont: LastChar = ' . $lastChar); + + $widthid = -1; + + if (!$font['isUnicode']) { + // With Unicode, widths array isn't used + + $this->numObj++; + $this->o_contents($this->numObj, 'new', 'raw'); + $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']'; + $widthid = $this->numObj; + } + + $missing_width = 500; + $stemV = 70; + + if (isset($font['MissingWidth'])) { + $missing_width = $font['MissingWidth']; + } + if (isset($font['StdVW'])) { + $stemV = $font['StdVW']; + } else { + if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) { + $stemV = 120; + } + } + + // load the pfb file, and put that into an object too. + // note that pdf supports only binary format type 1 font files, though there is a + // simple utility to convert them from pfa to pfb. + $data = file_get_contents($fbfile); + + // create the font descriptor + $this->numObj++; + $fontDescriptorId = $this->numObj; + + $this->numObj++; + $pfbid = $this->numObj; + + // determine flags (more than a little flakey, hopefully will not matter much) + $flags = 0; + + if ($font['ItalicAngle'] != 0) { + $flags += pow(2, 6); + } + + if ($font['IsFixedPitch'] === 'true') { + $flags += 1; + } + + $flags += pow(2, 5); // assume non-sybolic + $list = [ + 'Ascent' => 'Ascender', + 'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight' + 'MissingWidth' => 'MissingWidth', + 'Descent' => 'Descender', + 'FontBBox' => 'FontBBox', + 'ItalicAngle' => 'ItalicAngle' + ]; + $fdopt = [ + 'Flags' => $flags, + 'FontName' => $adobeFontName, + 'StemV' => $stemV + ]; + + foreach ($list as $k => $v) { + if (isset($font[$v])) { + $fdopt[$k] = $font[$v]; + } + } + + if ($isPfbFont) { + $fdopt['FontFile'] = $pfbid; + } elseif ($isTtfFont) { + $fdopt['FontFile2'] = $pfbid; + } + + $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt); + + // embed the font program + $this->o_contents($this->numObj, 'new'); + $this->objects[$pfbid]['c'] .= $data; + + // determine the cruicial lengths within this file + if ($isPfbFont) { + $l1 = strpos($data, 'eexec') + 6; + $l2 = strpos($data, '00000000') - $l1; + $l3 = mb_strlen($data, '8bit') - $l2 - $l1; + $this->o_contents( + $this->numObj, + 'add', + ['Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3] + ); + } elseif ($isTtfFont) { + $l1 = mb_strlen($data, '8bit'); + $this->o_contents($this->numObj, 'add', ['Length1' => $l1]); + } + + // tell the font object about all this new stuff + $options = [ + 'BaseFont' => $adobeFontName, + 'MissingWidth' => $missing_width, + 'Widths' => $widthid, + 'FirstChar' => $firstChar, + 'LastChar' => $lastChar, + 'FontDescriptor' => $fontDescriptorId + ]; + + if ($isTtfFont) { + $options['SubType'] = 'TrueType'; + } + + $this->addMessage("adding extra info to font.($fontObjId)"); + + foreach ($options as $fk => $fv) { + $this->addMessage("$fk : $fv"); + } + } + + return $options; + } + /** * A toUnicode section, needed for unicode fonts * @@ -962,20 +1273,20 @@ class Cpdf { switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'toUnicode' - ); + ]; break; case 'add': break; case 'out': - $ordering = '(UCS)'; - $registry = '(Adobe)'; + $ordering = 'UCS'; + $registry = 'Adobe'; if ($this->encrypted) { $this->encryptInit($id); $ordering = $this->ARC4($ordering); - $registry = $this->ARC4($registry); + $registry = $this->filterText($this->ARC4($registry), false, false); } $stream = <<> def /CMapName /Adobe-Identity-UCS def @@ -1027,7 +1338,7 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'fontDescriptor', 'info' => $options); + $this->objects[$id] = ['t' => 'fontDescriptor', 'info' => $options]; break; case 'out': @@ -1093,7 +1404,7 @@ EOT; switch ($action) { case 'new': // the options array should contain 'differences' and maybe 'encoding' - $this->objects[$id] = array('t' => 'fontEncoding', 'info' => $options); + $this->objects[$id] = ['t' => 'fontEncoding', 'info' => $options]; break; case 'out': @@ -1145,7 +1456,7 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'fontDescendentCID', 'info' => $options); + $this->objects[$id] = ['t' => 'fontDescendentCID', 'info' => $options]; // we need a CID system info section $cidSystemInfoId = ++$this->numObj; @@ -1231,15 +1542,15 @@ EOT; { switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'cidSystemInfo' - ); + ]; break; case 'add': break; case 'out': - $ordering = '(UCS)'; - $registry = '(Adobe)'; + $ordering = 'UCS'; + $registry = 'Adobe'; if ($this->encrypted) { $this->encryptInit($id); @@ -1250,12 +1561,12 @@ EOT; $res = "\n$id 0 obj\n"; - $res .= '<objects[$id] = array('t' => 'fontGIDtoCIDMap', 'info' => $options); + $this->objects[$id] = ['t' => 'fontGIDtoCIDMap', 'info' => $options]; break; case 'out': @@ -1336,7 +1647,7 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'procset', 'info' => array('PDF' => 1, 'Text' => 1)); + $this->objects[$id] = ['t' => 'procset', 'info' => ['PDF' => 1, 'Text' => 1]]; $this->o_pages($this->currentNode, 'procset', $id); $this->procsetObjectId = $id; break; @@ -1380,13 +1691,13 @@ EOT; case 'new': $this->infoObject = $id; $date = 'D:' . @date('Ymd'); - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'info', - 'info' => array( + 'info' => [ 'Producer' => 'CPDF (dompdf)', 'CreationDate' => $date - ) - ); + ] + ]; break; case 'Title': case 'Author': @@ -1449,10 +1760,10 @@ EOT; switch ($action) { case 'new': if (is_array($options)) { - $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => $options['type']); + $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => $options['type']]; } else { // then assume a URI action - $this->objects[$id] = array('t' => 'action', 'info' => $options, 'type' => 'URI'); + $this->objects[$id] = ['t' => 'action', 'info' => $options, 'type' => 'URI']; } break; @@ -1516,7 +1827,7 @@ EOT; // and add the action object which is going to be required switch ($options['type']) { case 'link': - $this->objects[$id] = array('t' => 'annotation', 'info' => $options); + $this->objects[$id] = ['t' => 'annotation', 'info' => $options]; $this->numObj++; $this->o_action($this->numObj, 'new', $options['url']); $this->objects[$id]['info']['actionId'] = $this->numObj; @@ -1525,9 +1836,9 @@ EOT; case 'ilink': // this is to a named internal link $label = $options['label']; - $this->objects[$id] = array('t' => 'annotation', 'info' => $options); + $this->objects[$id] = ['t' => 'annotation', 'info' => $options]; $this->numObj++; - $this->o_action($this->numObj, 'new', array('type' => 'ilink', 'label' => $label)); + $this->o_action($this->numObj, 'new', ['type' => 'ilink', 'label' => $label]); $this->objects[$id]['info']['actionId'] = $this->numObj; break; } @@ -1576,14 +1887,14 @@ EOT; switch ($action) { case 'new': $this->numPages++; - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'page', - 'info' => array( + 'info' => [ 'parent' => $this->currentNode, 'pageNum' => $this->numPages, 'mediaBox' => $this->objects[$this->currentNode]['info']['mediaBox'] - ) - ); + ] + ]; if (is_array($options)) { // then this must be a page insertion, array should contain 'rid','pos'=[before|after] @@ -1598,7 +1909,7 @@ EOT; $this->numObj++; $this->o_contents($this->numObj, 'new', $id); $this->currentContents = $this->numObj; - $this->objects[$id]['info']['contents'] = array(); + $this->objects[$id]['info']['contents'] = []; $this->objects[$id]['info']['contents'][] = $this->numObj; $match = ($this->numPages % 2 ? 'odd' : 'even'); @@ -1616,7 +1927,7 @@ EOT; case 'annot': // add an annotation to this page if (!isset($o['info']['annot'])) { - $o['info']['annot'] = array(); + $o['info']['annot'] = []; } // $options should contain the id of the annotation dictionary @@ -1686,7 +1997,7 @@ EOT; switch ($action) { case 'new': - $this->objects[$id] = array('t' => 'contents', 'c' => '', 'info' => array()); + $this->objects[$id] = ['t' => 'contents', 'c' => '', 'info' => []]; if (mb_strlen($options, '8bit') && intval($options)) { // then this contents is the primary for a page $this->objects[$id]['onPage'] = $options; @@ -1747,12 +2058,12 @@ EOT; { switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'embedjs', - 'info' => array( + 'info' => [ 'Names' => '[(EmbeddedJS) ' . ($id + 1) . ' 0 R]' - ) - ); + ] + ]; break; case 'out': @@ -1779,13 +2090,13 @@ EOT; { switch ($action) { case 'new': - $this->objects[$id] = array( + $this->objects[$id] = [ 't' => 'javascript', - 'info' => array( + 'info' => [ 'S' => '/JavaScript', 'JS' => '(' . $this->filterText($code, true, false) . ')', - ) - ); + ] + ]; break; case 'out': @@ -1816,7 +2127,7 @@ EOT; switch ($action) { case 'new': // make the new object - $this->objects[$id] = array('t' => 'image', 'data' => &$options['data'], 'info' => array()); + $this->objects[$id] = ['t' => 'image', 'data' => &$options['data'], 'info' => []]; $info =& $this->objects[$id]['info']; @@ -1835,10 +2146,10 @@ EOT; } switch ($options['channels']) { - case 1: + case 1: $info['ColorSpace'] = '/DeviceGray'; break; - case 4: + case 4: $info['ColorSpace'] = '/DeviceCMYK'; break; default: @@ -1917,7 +2228,7 @@ EOT; // assign it a place in the named resource dictionary as an external object, according to // the label passed in with it. - $this->o_pages($this->currentNode, 'xObject', array('label' => $options['label'], 'objNum' => $id)); + $this->o_pages($this->currentNode, 'xObject', ['label' => $options['label'], 'objNum' => $id]); // also make sure that we have the right procset object for it. $this->o_procset($this->procsetObjectId, 'add', 'ImageC'); @@ -1955,7 +2266,7 @@ EOT; */ protected function o_extGState($id, $action, $options = "") { - static $valid_params = array( + static $valid_params = [ "LW", "LC", "LC", @@ -1982,15 +2293,15 @@ EOT; "ca", "AIS", "TK" - ); + ]; switch ($action) { case "new": - $this->objects[$id] = array('t' => 'extGState', 'info' => $options); + $this->objects[$id] = ['t' => 'extGState', 'info' => $options]; // Tell the pages about the new resource $this->numStates++; - $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates)); + $this->o_pages($this->currentNode, 'extGState', ["objNum" => $id, "stateNum" => $this->numStates]); break; case "out": @@ -2012,6 +2323,362 @@ EOT; return null; } + /** + * @param integer $id + * @param string $action + * @param mixed $options + * @return string + */ + protected function o_xobject($id, $action, $options = '') + { + switch ($action) { + case 'new': + $this->objects[$id] = ['t' => 'xobject', 'info' => $options, 'c' => '']; + break; + + case 'procset': + $this->objects[$id]['procset'] = $options; + break; + + case 'font': + $this->objects[$id]['fonts'][$options['fontNum']] = [ + 'objNum' => $options['objNum'], + 'fontNum' => $options['fontNum'] + ]; + break; + + case 'xObject': + $this->objects[$id]['xObjects'][] = ['objNum' => $options['objNum'], 'label' => $options['label']]; + break; + + case 'out': + $o = &$this->objects[$id]; + $res = "\n$id 0 obj\n<< /Type /XObject\n"; + + foreach ($o["info"] as $k => $v) { + switch($k) + { + case 'Subtype': + $res .= "/Subtype /$v\n"; + break; + case 'bbox': + $res .= "/BBox ["; + foreach ($v as $value) { + $res .= sprintf("%.4F ", $value); + } + $res .= "]\n"; + break; + default: + $res .= "/$k $v\n"; + break; + } + } + $res .= "/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]\n"; + + $res .= "/Resources <<"; + if (isset($o['procset'])) { + $res .= "\n/ProcSet " . $o['procset'] . " 0 R"; + } else { + $res .= "\n/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]"; + } + if (isset($o['fonts']) && count($o['fonts'])) { + $res .= "\n/Font << "; + foreach ($o['fonts'] as $finfo) { + $res .= "\n/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R"; + } + $res .= "\n>>"; + } + if (isset($o['xObjects']) && count($o['xObjects'])) { + $res .= "\n/XObject << "; + foreach ($o['xObjects'] as $finfo) { + $res .= "\n/" . $finfo['label'] . " " . $finfo['objNum'] . " 0 R"; + } + $res .= "\n>>"; + } + $res .= "\n>>\n"; + + $tmp = $o["c"]; + if ($this->compressionReady && $this->options['compression']) { + // then implement ZLIB based compression on this content stream + $res .= " /Filter /FlateDecode\n"; + $tmp = gzcompress($tmp, 6); + } + + if ($this->encrypted) { + $this->encryptInit($id); + $tmp = $this->ARC4($tmp); + } + + $res .= "/Length " . mb_strlen($tmp, '8bit') . " >>\n"; + $res .= "stream\n" . $tmp . "\nendstream" . "\nendobj";; + + return $res; + } + + return null; + } + + /** + * @param $id + * @param $action + * @param string $options + * @return null|string + */ + protected function o_acroform($id, $action, $options = '') + { + switch ($action) { + case "new": + $this->o_catalog($this->catalogId, 'acroform', $id); + $this->objects[$id] = array('t' => 'acroform', 'info' => $options); + break; + + case 'addfield': + $this->objects[$id]['info']['Fields'][] = $options; + break; + + case 'font': + $this->objects[$id]['fonts'][$options['fontNum']] = [ + 'objNum' => $options['objNum'], + 'fontNum' => $options['fontNum'] + ]; + break; + + case "out": + $o = &$this->objects[$id]; + $res = "\n$id 0 obj\n<<"; + + foreach ($o["info"] as $k => $v) { + switch($k) { + case 'Fields': + $res .= " /Fields ["; + foreach ($v as $i) { + $res .= "$i 0 R "; + } + $res .= "]\n"; + break; + default: + $res .= "/$k $v\n"; + } + } + + $res .= "/DR <<\n"; + if (isset($o['fonts']) && count($o['fonts'])) { + $res .= "/Font << \n"; + foreach ($o['fonts'] as $finfo) { + $res .= "/F" . $finfo['fontNum'] . " " . $finfo['objNum'] . " 0 R\n"; + } + $res .= ">>\n"; + } + $res .= ">>\n"; + + $res .= ">>\nendobj"; + + return $res; + } + + return null; + } + + /** + * @param $id + * @param $action + * @param mixed $options + * @return null|string + */ + protected function o_field($id, $action, $options = '') + { + switch ($action) { + case "new": + $this->o_page($options['pageid'], 'annot', $id); + $this->o_acroform($this->acroFormId, 'addfield', $id); + $this->objects[$id] = ['t' => 'field', 'info' => $options]; + break; + + case 'set': + $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options); + break; + + case "out": + $o = &$this->objects[$id]; + $res = "\n$id 0 obj\n<< /Type /Annot /Subtype /Widget \n"; + + $encrypted = $this->encrypted; + if ($encrypted) { + $this->encryptInit($id); + } + + foreach ($o["info"] as $k => $v) { + switch ($k) { + case 'pageid': + $res .= "/P $v 0 R\n"; + break; + case 'value': + if ($encrypted) { + $v = $this->filterText($this->ARC4($v), false, false); + } + $res .= "/V ($v)\n"; + break; + case 'refvalue': + $res .= "/V $v 0 R\n"; + break; + case 'da': + if ($encrypted) { + $v = $this->filterText($this->ARC4($v), false, false); + } + $res .= "/DA ($v)\n"; + break; + case 'options': + $res .= "/Opt [\n"; + foreach ($v as $opt) { + if ($encrypted) { + $opt = $this->filterText($this->ARC4($opt), false, false); + } + $res .= "($opt)\n"; + } + $res .= "]\n"; + break; + case 'rect': + $res .= "/Rect ["; + foreach ($v as $value) { + $res .= sprintf("%.4F ", $value); + } + $res .= "]\n"; + break; + case 'appearance': + $res .= "/AP << "; + foreach ($v as $a => $ref) { + $res .= "/$a $ref 0 R "; + } + $res .= ">>\n"; + break; + case 'T': + if($encrypted) { + $v = $this->filterText($this->ARC4($v), false, false); + } + $res .= "/T ($v)\n"; + break; + default: + $res .= "/$k $v\n"; + } + + } + + $res .= ">>\nendobj"; + + return $res; + } + + return null; + } + + /** + * + * @param $id + * @param $action + * @param string $options + * @return null|string + */ + protected function o_sig($id, $action, $options = '') + { + $sign_maxlen = $this->signatureMaxLen; + + switch ($action) { + case "new": + $this->objects[$id] = array('t' => 'sig', 'info' => $options); + $this->byteRange[$id] = ['t' => 'sig']; + break; + + case 'byterange': + $o = &$this->objects[$id]; + $content =& $options['content']; + $content_len = strlen($content); + $pos = strpos($content, sprintf("/ByteRange [ %'.010d", $id)); + $len = strlen('/ByteRange [ ********** ********** ********** ********** ]'); + $rangeStartPos = $pos + $len + 1 + 10; // before '<' + $content = substr_replace($content, str_pad(sprintf('/ByteRange [ 0 %u %u %u ]', $rangeStartPos, $rangeStartPos + $sign_maxlen + 2, $content_len - 2 - $sign_maxlen - $rangeStartPos ), $len, ' ', STR_PAD_RIGHT), $pos, $len); + + $fuid = uniqid(); + $tmpInput = $this->tmp . "/pkcs7.tmp." . $fuid . '.in'; + $tmpOutput = $this->tmp . "/pkcs7.tmp." . $fuid . '.out'; + + if (file_put_contents($tmpInput, substr($content, 0, $rangeStartPos)) === false) { + throw new \Exception("Unable to write temporary file for signing."); + } + if (file_put_contents($tmpInput, substr($content, $rangeStartPos + 2 + $sign_maxlen), + FILE_APPEND) === false) { + throw new \Exception("Unable to write temporary file for signing."); + } + + if (openssl_pkcs7_sign($tmpInput, $tmpOutput, + $o['info']['SignCert'], + array($o['info']['PrivKey'], $o['info']['Password']), + array(), PKCS7_BINARY | PKCS7_DETACHED) === false) { + throw new \Exception("Failed to prepare signature."); + } + + $signature = file_get_contents($tmpOutput); + + unlink($tmpInput); + unlink($tmpOutput); + + $sign = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); + list($head, $signature) = explode("\n\n", $sign); + + $signature = base64_decode(trim($signature)); + + $signature = current(unpack('H*', $signature)); + $signature = str_pad($signature, $sign_maxlen, '0'); + $siglen = strlen($signature); + if (strlen($signature) > $sign_maxlen) { + throw new \Exception("Signature length ($siglen) exceeds the $sign_maxlen limit."); + } + + $content = substr_replace($content, $signature, $rangeStartPos + 1, $sign_maxlen); + break; + + case "out": + $res = "\n$id 0 obj\n<<\n"; + + $encrypted = $this->encrypted; + if ($encrypted) { + $this->encryptInit($id); + } + + $res .= "/ByteRange " .sprintf("[ %'.010d ********** ********** ********** ]\n", $id); + $res .= "/Contents <" . str_pad('', $sign_maxlen, '0') . ">\n"; + $res .= "/Filter/Adobe.PPKLite\n"; //PPKMS \n"; + $res .= "/Type/Sig/SubFilter/adbe.pkcs7.detached \n"; + + $date = "D:" . substr_replace(date('YmdHisO'), '\'', -2, 0) . '\''; + if ($encrypted) { + $date = $this->ARC4($date); + } + + $res .= "/M ($date)\n"; + $res .= "/Prop_Build << /App << /Name /DomPDF >> /Filter << /Name /Adobe.PPKLite >> >>\n"; + + $o = &$this->objects[$id]; + foreach ($o['info'] as $k => $v) { + switch($k) { + case 'Name': + case 'Location': + case 'Reason': + case 'ContactInfo': + if ($v !== null && $v !== '') { + $res .= "/$k (" . + ($encrypted ? $this->filterText($this->ARC4($v), false, false) : $v) . ") \n"; + } + break; + } + } + $res .= ">>\nendobj"; + + return $res; + } + + return null; + } + /** * encryption object. * @@ -2025,7 +2692,7 @@ EOT; switch ($action) { case 'new': // make the new object - $this->objects[$id] = array('t' => 'encryption', 'info' => $options); + $this->objects[$id] = ['t' => 'encryption', 'info' => $options]; $this->arc4_objnum = $id; break; @@ -2101,6 +2768,154 @@ EOT; return null; } + protected function o_indirect_references($id, $action, $options = null) + { + switch ($action) { + case 'new': + case 'add': + if ($id === 0) { + $id = ++$this->numObj; + $this->o_catalog($this->catalogId, 'names', $id); + $this->objects[$id] = ['t' => 'indirect_references', 'info' => $options]; + $this->indirectReferenceId = $id; + } else { + $this->objects[$id]['info'] = array_merge($this->objects[$id]['info'], $options); + } + break; + case 'out': + $res = "\n$id 0 obj << "; + + foreach($this->objects[$id]['info'] as $referenceObjName => $referenceObjId) { + $res .= "/$referenceObjName $referenceObjId 0 R "; + } + + $res .= ">> endobj"; + return $res; + } + + return null; + } + + protected function o_names($id, $action, $options = null) + { + switch ($action) { + case 'new': + case 'add': + if ($id === 0) { + $id = ++$this->numObj; + $this->objects[$id] = ['t' => 'names', 'info' => [$options]]; + $this->o_indirect_references($this->indirectReferenceId, 'add', ['EmbeddedFiles' => $id]); + $this->embeddedFilesId = $id; + } else { + $this->objects[$id]['info'][] = $options; + } + break; + case 'out': + $info = &$this->objects[$id]['info']; + $res = ''; + if (count($info) > 0) { + $res = "\n$id 0 obj << /Names [ "; + + if ($this->encrypted) { + $this->encryptInit($id); + } + + foreach ($info as $entry) { + if ($this->encrypted) { + $filename = $this->ARC4($entry['filename']); + } else { + $filename = $entry['filename']; + } + + $res .= "($filename) " . $entry['dict_reference'] . " 0 R "; + } + + $res .= "] >> endobj"; + } + return $res; + } + + return null; + } + + protected function o_embedded_file_dictionary($id, $action, $options = null) + { + switch ($action) { + case 'new': + $embeddedFileId = ++$this->numObj; + $options['embedded_reference'] = $embeddedFileId; + $this->objects[$id] = ['t' => 'embedded_file_dictionary', 'info' => $options]; + $this->o_embedded_file($embeddedFileId, 'new', $options); + $options['dict_reference'] = $id; + $this->o_names($this->embeddedFilesId, 'add', $options); + break; + case 'out': + $info = &$this->objects[$id]['info']; + + if ($this->encrypted) { + $this->encryptInit($id); + $filename = $this->ARC4($info['filename']); + $description = $this->ARC4($info['description']); + } else { + $filename = $info['filename']; + $description = $info['description']; + } + + $res = "\n$id 0 obj <>"; + $res .= " /F ($filename) /UF ($filename) /Desc ($description)"; + $res .= " >> endobj"; + return $res; + } + + return null; + } + + protected function o_embedded_file($id, $action, $options = null): ?string + { + switch ($action) { + case 'new': + $this->objects[$id] = ['t' => 'embedded_file', 'info' => $options]; + break; + case 'out': + $info = &$this->objects[$id]['info']; + + if ($this->compressionReady) { + $filepath = $info['filepath']; + $checksum = md5_file($filepath); + $f = fopen($filepath, "rb"); + + $file_content_compressed = ''; + $deflateContext = deflate_init(ZLIB_ENCODING_DEFLATE, ['level' => 6]); + while (($block = fread($f, 8192))) { + $file_content_compressed .= deflate_add($deflateContext, $block, ZLIB_NO_FLUSH); + } + $file_content_compressed .= deflate_add($deflateContext, '', ZLIB_FINISH); + $file_size_uncompressed = ftell($f); + fclose($f); + } else { + $file_content = file_get_contents($info['filepath']); + $file_size_uncompressed = mb_strlen($file_content, '8bit'); + $checksum = md5($file_content); + } + + if ($this->encrypted) { + $this->encryptInit($id); + $checksum = $this->ARC4($checksum); + $file_content_compressed = $this->ARC4($file_content_compressed); + } + $file_size_compressed = mb_strlen($file_content_compressed, '8bit'); + + $res = "\n$id 0 obj <>" . + " /Type/EmbeddedFile /Filter/FlateDecode" . + " /Length $file_size_compressed >> stream\n$file_content_compressed\nendstream\nendobj"; + + return $res; + } + + return null; + } + /** * ARC4 functions * A series of function to implement ARC4 encoding in PHP @@ -2221,7 +3036,7 @@ EOT; function addLink($url, $x0, $y0, $x1, $y1) { $this->numObj++; - $info = array('type' => 'link', 'url' => $url, 'rect' => array($x0, $y0, $x1, $y1)); + $info = ['type' => 'link', 'url' => $url, 'rect' => [$x0, $y0, $x1, $y1]]; $this->o_annotation($this->numObj, 'new', $info); } @@ -2237,7 +3052,7 @@ EOT; function addInternalLink($label, $x0, $y0, $x1, $y1) { $this->numObj++; - $info = array('type' => 'ilink', 'label' => $label, 'rect' => array($x0, $y0, $x1, $y1)); + $info = ['type' => 'ilink', 'label' => $label, 'rect' => [$x0, $y0, $x1, $y1]]; $this->o_annotation($this->numObj, 'new', $info); } @@ -2250,11 +3065,11 @@ EOT; * @param string $ownerPass * @param array $pc */ - function setEncryption($userPass = '', $ownerPass = '', $pc = array()) + function setEncryption($userPass = '', $ownerPass = '', $pc = []) { $p = bindec("11000000"); - $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32); + $options = ['print' => 4, 'modify' => 8, 'copy' => 16, 'add' => 32]; foreach ($pc as $k => $v) { if ($v && isset($options[$k])) { @@ -2274,7 +3089,7 @@ EOT; $ownerPass = $userPass; } - $this->o_encryption($this->numObj, 'new', array('user' => $userPass, 'owner' => $ownerPass, 'p' => $p)); + $this->o_encryption($this->numObj, 'new', ['user' => $userPass, 'owner' => $ownerPass, 'p' => $p]); } } @@ -2307,11 +3122,11 @@ EOT; $id = $this->catalogId; - $this->o_catalog($id, 'javascript', $js_id); + $this->o_indirect_references($this->indirectReferenceId, 'add', ['JavaScript' => $js_id]); } if ($this->fileIdentifier === '') { - $tmp = implode('', $this->objects[$this->infoObject]['info']); + $tmp = implode('', $this->objects[$this->infoObject]['info']); $this->fileIdentifier = md5('DOMPDF' . __FILE__ . $tmp . microtime() . mt_rand()); } @@ -2322,10 +3137,17 @@ EOT; $this->checkAllHere(); - $xref = array(); - $content = '%PDF-1.3'; + $xref = []; + $content = '%PDF-' . self::PDF_VERSION; $pos = mb_strlen($content, '8bit'); + // pre-process o_font objects before output of all objects + foreach ($this->objects as $k => $v) { + if ($v['t'] === 'font') { + $this->o_font($k, 'add'); + } + } + foreach ($this->objects as $k => $v) { $tmp = 'o_' . $v['t']; $cont = $this->$tmp($k, 'out'); @@ -2358,6 +3180,13 @@ EOT; $content .= ">>\nstartxref\n$pos\n%%EOF\n"; + if (count($this->byteRange) > 0) { + foreach ($this->byteRange as $k => $v) { + $tmp = 'o_' . $v['t']; + $this->$tmp($k, 'byterange', ['content' => &$content]); + } + } + return $content; } @@ -2368,10 +3197,10 @@ EOT; * * @param array $pageSize */ - private function newDocument($pageSize = array(0, 0, 612, 792)) + private function newDocument($pageSize = [0, 0, 612, 792]) { $this->numObj = 0; - $this->objects = array(); + $this->objects = []; $this->numObj++; $this->o_catalog($this->numObj, 'new'); @@ -2420,7 +3249,7 @@ EOT; } //$name filename without folder and extension of font metrics - //$dir folder of font metrics + //$dir folder of font metrics //$fontcache folder of runtime created php serialized version of font metrics. // If this is not given, the same folder as the font metrics will be used. // Storing and reusing serialized versions improves speed much @@ -2463,10 +3292,10 @@ EOT; if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) { // then rebuild the php_.afm file from the .afm file $this->addMessage("openFont: build php file from $dir$metrics_name"); - $data = array(); + $data = []; // 20 => 'space' - $data['codeToName'] = array(); + $data['codeToName'] = []; // Since we're not going to enable Unicode for the core fonts we need to use a font-based // setting for Unicode support rather than a global setting. @@ -2517,7 +3346,7 @@ EOT; //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; case 'C': // Found in AFM files $bits = explode(';', trim($row)); - $dtmp = array(); + $dtmp = ['C' => null, 'N' => null, 'WX' => null, 'B' => []]; foreach ($bits as $bit) { $bits2 = explode(' ', trim($bit)); @@ -2526,7 +3355,7 @@ EOT; } if (count($bits2) > 2) { - $dtmp[$bits2[0]] = array(); + $dtmp[$bits2[0]] = []; for ($i = 1; $i < count($bits2); $i++) { $dtmp[$bits2[0]][] = $bits2[$i]; } @@ -2542,15 +3371,15 @@ EOT; $width = floatval($dtmp['WX']); if ($c >= 0) { - if ($c != hexdec($n)) { + if (!ctype_xdigit($n) || $c != hexdec($n)) { $data['codeToName'][$c] = $n; } $data['C'][$c] = $width; - } else { + } elseif (isset($n)) { $data['C'][$n] = $width; } - if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') { + if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') { $data['MissingWidth'] = $width; } @@ -2563,7 +3392,7 @@ EOT; } $bits = explode(';', trim($row)); - $dtmp = array(); + $dtmp = ['G' => null, 'N' => null, 'U' => null, 'WX' => null]; foreach ($bits as $bit) { $bits2 = explode(' ', trim($bit)); @@ -2572,7 +3401,7 @@ EOT; } if (count($bits2) > 2) { - $dtmp[$bits2[0]] = array(); + $dtmp[$bits2[0]] = []; for ($i = 1; $i < count($bits2); $i++) { $dtmp[$bits2[0]][] = $bits2[$i]; } @@ -2595,15 +3424,15 @@ EOT; $cidtogid[$c * 2 + 1] = chr($glyph & 0xFF); } - if ($c != hexdec($n)) { + if (!ctype_xdigit($n) || $c != hexdec($n)) { $data['codeToName'][$c] = $n; } $data['C'][$c] = $width; - } else { + } elseif (isset($n)) { $data['C'][$n] = $width; } - if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') { + if (!isset($data['MissingWidth']) && $c === -1 && $n === '.notdef') { $data['MissingWidth'] = $width; } @@ -2653,10 +3482,16 @@ EOT; * @param $fontName * @param string $encoding * @param bool $set + * @param bool $isSubsetting * @return int + * @throws FontNotFoundException */ - function selectFont($fontName, $encoding = '', $set = true) + function selectFont($fontName, $encoding = '', $set = true, $isSubsetting = true) { + if ($fontName === null || $fontName === '') { + return $this->currentFontNum; + } + $ext = substr($fontName, -4); if ($ext === '.afm' || $ext === '.ufm') { $fontName = substr($fontName, 0, mb_strlen($fontName) - 4); @@ -2675,8 +3510,7 @@ EOT; $font = &$this->fonts[$fontName]; $name = basename($fontName); - $dir = dirname($fontName) . '/'; - $options = array('name' => $name, 'fontFileName' => $fontName); + $options = ['name' => $name, 'fontFileName' => $fontName, 'isSubsetting' => $isSubsetting]; if (is_array($encoding)) { // then encoding and differences might be set @@ -2694,286 +3528,25 @@ EOT; } } - $fontObj = $this->numObj; $this->o_font($this->numObj, 'new', $options); - $font['fontNum'] = $this->numFonts; - // if this is a '.afm' font, and there is a '.pfa' file to go with it (as there - // should be for all non-basic fonts), then load it into an object and put the - // references into the font object - $basefile = $fontName; - - $fbtype = ''; - if (file_exists("$basefile.ttf")) { - $fbtype = 'ttf'; - } elseif (file_exists("$basefile.TTF")) { - $fbtype = 'TTF'; - } elseif (file_exists("$basefile.pfb")) { - $fbtype = 'pfb'; - } elseif (file_exists("$basefile.PFB")) { - $fbtype = 'PFB'; - } - - $fbfile = "$basefile.$fbtype"; - - // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb'; - // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf'; - $this->addMessage('selectFont: checking for - ' . $fbfile); - - // OAR - I don't understand this old check - // if (substr($fontName, -4) === '.afm' && strlen($fbtype)) { - if ($fbtype) { - $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName']; - // $fontObj = $this->numObj; - $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName"); - - // find the array of font widths, and put that into an object. - $firstChar = -1; - $lastChar = 0; - $widths = array(); - $cid_widths = array(); - - foreach ($font['C'] as $num => $d) { - if (intval($num) > 0 || $num == '0') { - if (!$font['isUnicode']) { - // With Unicode, widths array isn't used - if ($lastChar > 0 && $num > $lastChar + 1) { - for ($i = $lastChar + 1; $i < $num; $i++) { - $widths[] = 0; - } - } - } - - $widths[] = $d; - - if ($font['isUnicode']) { - $cid_widths[$num] = $d; - } - - if ($firstChar == -1) { - $firstChar = $num; - } - - $lastChar = $num; - } - } - - // also need to adjust the widths for the differences array - if (isset($options['differences'])) { - foreach ($options['differences'] as $charNum => $charName) { - if ($charNum > $lastChar) { - if (!$font['isUnicode']) { - // With Unicode, widths array isn't used - for ($i = $lastChar + 1; $i <= $charNum; $i++) { - $widths[] = 0; - } - } - - $lastChar = $charNum; - } - - if (isset($font['C'][$charName])) { - $widths[$charNum - $firstChar] = $font['C'][$charName]; - if ($font['isUnicode']) { - $cid_widths[$charName] = $font['C'][$charName]; - } - } - } - } - - if ($font['isUnicode']) { - $font['CIDWidths'] = $cid_widths; - } - - $this->addMessage('selectFont: FirstChar = ' . $firstChar); - $this->addMessage('selectFont: LastChar = ' . $lastChar); - - $widthid = -1; - - if (!$font['isUnicode']) { - // With Unicode, widths array isn't used - - $this->numObj++; - $this->o_contents($this->numObj, 'new', 'raw'); - $this->objects[$this->numObj]['c'] .= '[' . implode(' ', $widths) . ']'; - $widthid = $this->numObj; - } - - $missing_width = 500; - $stemV = 70; - - if (isset($font['MissingWidth'])) { - $missing_width = $font['MissingWidth']; - } - if (isset($font['StdVW'])) { - $stemV = $font['StdVW']; - } else { - if (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) { - $stemV = 120; - } - } - - // load the pfb file, and put that into an object too. - // note that pdf supports only binary format type 1 font files, though there is a - // simple utility to convert them from pfa to pfb. - // FIXME: should we move font subset creation to CPDF::output? See notes in issue #750. - if (!$this->isUnicode || strtolower($fbtype) !== 'ttf' || empty($this->stringSubsets)) { - $data = file_get_contents($fbfile); - } else { - $this->stringSubsets[$fontName][] = 32; // Force space if not in yet - - $subset = $this->stringSubsets[$fontName]; - sort($subset); - - // Load font - $font_obj = Font::load($fbfile); - $font_obj->parse(); - - // Define subset - $font_obj->setSubset($subset); - $font_obj->reduce(); - - // Write new font - $tmp_name = $this->tmp . "/" . basename($fbfile) . ".tmp." . uniqid(); - $font_obj->open($tmp_name, BinaryStream::modeWrite); - $font_obj->encode(array("OS/2")); - $font_obj->close(); - - // Parse the new font to get cid2gid and widths - $font_obj = Font::load($tmp_name); - - // Find Unicode char map table - $subtable = null; - foreach ($font_obj->getData("cmap", "subtables") as $_subtable) { - if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) { - $subtable = $_subtable; - break; - } - } - - if ($subtable) { - $glyphIndexArray = $subtable["glyphIndexArray"]; - $hmtx = $font_obj->getData("hmtx"); - - unset($glyphIndexArray[0xFFFF]); - - $cidtogid = str_pad('', max(array_keys($glyphIndexArray)) * 2 + 1, "\x00"); - $font['CIDWidths'] = array(); - foreach ($glyphIndexArray as $cid => $gid) { - if ($cid >= 0 && $cid < 0xFFFF && $gid) { - $cidtogid[$cid * 2] = chr($gid >> 8); - $cidtogid[$cid * 2 + 1] = chr($gid & 0xFF); - } - - $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]); - $font['CIDWidths'][$cid] = $width; - } - - $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid)); - $font['CIDtoGID_Compressed'] = true; - - $data = file_get_contents($tmp_name); - } else { - $data = file_get_contents($fbfile); - } - - $font_obj->close(); - unlink($tmp_name); - } - - // create the font descriptor - $this->numObj++; - $fontDescriptorId = $this->numObj; - - $this->numObj++; - $pfbid = $this->numObj; - - // determine flags (more than a little flakey, hopefully will not matter much) - $flags = 0; - - if ($font['ItalicAngle'] != 0) { - $flags += pow(2, 6); - } - - if ($font['IsFixedPitch'] === 'true') { - $flags += 1; - } - - $flags += pow(2, 5); // assume non-sybolic - $list = array( - 'Ascent' => 'Ascender', - 'CapHeight' => 'Ascender', //FIXME: php-font-lib is not grabbing this value, so we'll fake it and use the Ascender value // 'CapHeight' - 'MissingWidth' => 'MissingWidth', - 'Descent' => 'Descender', - 'FontBBox' => 'FontBBox', - 'ItalicAngle' => 'ItalicAngle' - ); - $fdopt = array( - 'Flags' => $flags, - 'FontName' => $adobeFontName, - 'StemV' => $stemV - ); - - foreach ($list as $k => $v) { - if (isset($font[$v])) { - $fdopt[$k] = $font[$v]; - } - } - - if (strtolower($fbtype) === 'pfb') { - $fdopt['FontFile'] = $pfbid; - } elseif (strtolower($fbtype) === 'ttf') { - $fdopt['FontFile2'] = $pfbid; - } - - $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt); - - // embed the font program - $this->o_contents($this->numObj, 'new'); - $this->objects[$pfbid]['c'] .= $data; - - // determine the cruicial lengths within this file - if (strtolower($fbtype) === 'pfb') { - $l1 = strpos($data, 'eexec') + 6; - $l2 = strpos($data, '00000000') - $l1; - $l3 = mb_strlen($data, '8bit') - $l2 - $l1; - $this->o_contents( - $this->numObj, - 'add', - array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3) - ); - } elseif (strtolower($fbtype) == 'ttf') { - $l1 = mb_strlen($data, '8bit'); - $this->o_contents($this->numObj, 'add', array('Length1' => $l1)); - } - - // tell the font object about all this new stuff - $tmp = array( - 'BaseFont' => $adobeFontName, - 'MissingWidth' => $missing_width, - 'Widths' => $widthid, - 'FirstChar' => $firstChar, - 'LastChar' => $lastChar, - 'FontDescriptor' => $fontDescriptorId - ); - - if (strtolower($fbtype) === 'ttf') { - $tmp['SubType'] = 'TrueType'; - } - - $this->addMessage("adding extra info to font.($fontObj)"); - - foreach ($tmp as $fk => $fv) { - $this->addMessage("$fk : $fv"); - } - - $this->o_font($fontObj, 'add', $tmp); + if (file_exists("$fontName.ttf")) { + $fileSuffix = 'ttf'; + } elseif (file_exists("$fontName.TTF")) { + $fileSuffix = 'TTF'; + } elseif (file_exists("$fontName.pfb")) { + $fileSuffix = 'pfb'; + } elseif (file_exists("$fontName.PFB")) { + $fileSuffix = 'PFB'; } else { - $this->addMessage( - 'selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts' - ); + $fileSuffix = ''; } + $font['fileSuffix'] = $fileSuffix; + + $font['fontNum'] = $this->numFonts; + $font['isSubsetting'] = $isSubsetting && $font['isUnicode'] && strtolower($fileSuffix) === 'ttf'; + // also set the differences here, note that this means that these will take effect only the //first time that a font is selected, else they are ignored if (isset($options['differences'])) { @@ -2990,12 +3563,9 @@ EOT; // applied to it as well. $this->currentFont = $this->currentBaseFont; $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum']; - - //$this->setCurrentFont(); } return $this->currentFontNum; - //return $this->numObj; } /** @@ -3062,7 +3632,7 @@ EOT; */ function setColor($color, $force = false) { - $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null); + $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null]; if (!$force && $this->currentColor == $new_color) { return; @@ -3086,7 +3656,7 @@ EOT; */ function setFillRule($fillRule) { - if (!in_array($fillRule, array("nonzero", "evenodd"))) { + if (!in_array($fillRule, ["nonzero", "evenodd"])) { return; } @@ -3101,7 +3671,7 @@ EOT; */ function setStrokeColor($color, $force = false) { - $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null); + $new_color = [$color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null]; if (!$force && $this->currentStrokeColor == $new_color) { return; @@ -3149,7 +3719,7 @@ EOT; */ function setLineTransparency($mode, $opacity) { - static $blend_modes = array( + static $blend_modes = [ "Normal", "Multiply", "Screen", @@ -3162,26 +3732,30 @@ EOT; "SoftLight", "Difference", "Exclusion" - ); + ]; if (!in_array($mode, $blend_modes)) { $mode = "Normal"; } - // Only create a new graphics state if required - if ($mode === $this->currentLineTransparency["mode"] && - $opacity == $this->currentLineTransparency["opacity"] - ) { + if (is_null($this->currentLineTransparency)) { + $this->currentLineTransparency = []; + } + + if ($mode === (key_exists('mode', $this->currentLineTransparency) ? + $this->currentLineTransparency['mode'] : '') && + $opacity === (key_exists('opacity', $this->currentLineTransparency) ? + $this->currentLineTransparency["opacity"] : '')) { return; } $this->currentLineTransparency["mode"] = $mode; $this->currentLineTransparency["opacity"] = $opacity; - $options = array( + $options = [ "BM" => "/$mode", "CA" => (float)$opacity - ); + ]; $this->setGraphicsState($options); } @@ -3200,7 +3774,7 @@ EOT; */ function setFillTransparency($mode, $opacity) { - static $blend_modes = array( + static $blend_modes = [ "Normal", "Multiply", "Screen", @@ -3213,25 +3787,30 @@ EOT; "SoftLight", "Difference", "Exclusion" - ); + ]; if (!in_array($mode, $blend_modes)) { $mode = "Normal"; } - if ($mode === $this->currentFillTransparency["mode"] && - $opacity == $this->currentFillTransparency["opacity"] - ) { + if (is_null($this->currentFillTransparency)) { + $this->currentFillTransparency = []; + } + + if ($mode === (key_exists('mode', $this->currentFillTransparency) ? + $this->currentFillTransparency['mode'] : '') && + $opacity === (key_exists('opacity', $this->currentFillTransparency) ? + $this->currentFillTransparency["opacity"] : '')) { return; } $this->currentFillTransparency["mode"] = $mode; $this->currentFillTransparency["opacity"] = $opacity; - $options = array( + $options = [ "BM" => "/$mode", "ca" => (float)$opacity, - ); + ]; $this->setGraphicsState($options); } @@ -3344,7 +3923,8 @@ EOT; /** * draw a bezier curve based on 4 control points - */ function quadTo($cpx, $cpy, $x, $y) + */ + function quadTo($cpx, $cpy, $x, $y) { $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F v", $cpx, $cpy, $x, $y)); } @@ -3510,13 +4090,13 @@ EOT; $string .= "$width w"; } - $ca = array('butt' => 0, 'round' => 1, 'square' => 2); + $ca = ['butt' => 0, 'round' => 1, 'square' => 2]; if (isset($ca[$cap])) { $string .= " $ca[$cap] J"; } - $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); + $ja = ['miter' => 0, 'round' => 1, 'bevel' => 2]; if (isset($ja[$join])) { $string .= " $ja[$join] j"; @@ -3594,9 +4174,9 @@ EOT; $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re", $x1, $y1, $width, $height)); } - function stroke() + function stroke(bool $close = false) { - $this->addContent("\nS"); + $this->addContent("\n" . ($close ? "s" : "S")); } function fill() @@ -3604,9 +4184,190 @@ EOT; $this->addContent("\nf" . ($this->fillRule === "evenodd" ? "*" : "")); } - function fillStroke() + function fillStroke(bool $close = false) { - $this->addContent("\nb" . ($this->fillRule === "evenodd" ? "*" : "")); + $this->addContent("\n" . ($close ? "b" : "B") . ($this->fillRule === "evenodd" ? "*" : "")); + } + + /** + * @param string $subtype + * @param integer $x + * @param integer $y + * @param integer $w + * @param integer $h + * @return int + */ + function addXObject($subtype, $x, $y, $w, $h) + { + $id = ++$this->numObj; + $this->o_xobject($id, 'new', ['Subtype' => $subtype, 'bbox' => [$x, $y, $w, $h]]); + return $id; + } + + /** + * @param integer $numXObject + * @param string $type + * @param array $options + */ + function setXObjectResource($numXObject, $type, $options) + { + if (in_array($type, ['procset', 'font', 'xObject'])) { + $this->o_xobject($numXObject, $type, $options); + } + } + + /** + * add signature + * + * $fieldSigId = $cpdf->addFormField(Cpdf::ACROFORM_FIELD_SIG, 'Signature1', 0, 0, 0, 0, 0); + * + * $signatureId = $cpdf->addSignature([ + * 'signcert' => file_get_contents('dompdf.crt'), + * 'privkey' => file_get_contents('dompdf.key'), + * 'password' => 'password', + * 'name' => 'DomPDF DEMO', + * 'location' => 'Home', + * 'reason' => 'First Form', + * 'contactinfo' => 'info' + * ]); + * $cpdf->setFormFieldValue($fieldSigId, "$signatureId 0 R"); + * + * @param string $signcert + * @param string $privkey + * @param string $password + * @param string|null $name + * @param string|null $location + * @param string|null $reason + * @param string|null $contactinfo + * @return int + */ + function addSignature($signcert, $privkey, $password = '', $name = null, $location = null, $reason = null, $contactinfo = null) { + $sigId = ++$this->numObj; + $this->o_sig($sigId, 'new', [ + 'SignCert' => $signcert, + 'PrivKey' => $privkey, + 'Password' => $password, + 'Name' => $name, + 'Location' => $location, + 'Reason' => $reason, + 'ContactInfo' => $contactinfo + ]); + + return $sigId; + } + + /** + * add field to form + * + * @param string $type ACROFORM_FIELD_* + * @param string $name + * @param $x0 + * @param $y0 + * @param $x1 + * @param $y1 + * @param integer $ff Field Flag ACROFORM_FIELD_*_* + * @param float $size + * @param array $color + * @return int + */ + public function addFormField($type, $name, $x0, $y0, $x1, $y1, $ff = 0, $size = 10.0, $color = [0, 0, 0]) + { + if (!$this->numFonts) { + $this->selectFont($this->defaultFont); + } + + $color = implode(' ', $color) . ' rg'; + + $currentFontNum = $this->currentFontNum; + $font = array_filter($this->objects[$this->currentNode]['info']['fonts'], + function($item) use ($currentFontNum) { return $item['fontNum'] == $currentFontNum; }); + + $this->o_acroform($this->acroFormId, 'font', + ['objNum' => $font[0]['objNum'], 'fontNum' => $font[0]['fontNum']]); + + $fieldId = ++$this->numObj; + $this->o_field($fieldId, 'new', [ + 'rect' => [$x0, $y0, $x1, $y1], + 'F' => 4, + 'FT' => "/$type", + 'T' => $name, + 'Ff' => $ff, + 'pageid' => $this->currentPage, + 'da' => "$color /F$this->currentFontNum " . sprintf('%.1F Tf ', $size) + ]); + + return $fieldId; + } + + /** + * set Field value + * + * @param integer $numFieldObj + * @param string $value + */ + public function setFormFieldValue($numFieldObj, $value) + { + $this->o_field($numFieldObj, 'set', ['value' => $value]); + } + + /** + * set Field value (reference) + * + * @param integer $numFieldObj + * @param integer $numObj Object number + */ + public function setFormFieldRefValue($numFieldObj, $numObj) + { + $this->o_field($numFieldObj, 'set', ['refvalue' => $numObj]); + } + + /** + * set Field Appearanc (reference) + * + * @param integer $numFieldObj + * @param integer $normalNumObj + * @param integer|null $rolloverNumObj + * @param integer|null $downNumObj + */ + public function setFormFieldAppearance($numFieldObj, $normalNumObj, $rolloverNumObj = null, $downNumObj = null) + { + $appearance['N'] = $normalNumObj; + + if ($rolloverNumObj !== null) { + $appearance['R'] = $rolloverNumObj; + } + + if ($downNumObj !== null) { + $appearance['D'] = $downNumObj; + } + + $this->o_field($numFieldObj, 'set', ['appearance' => $appearance]); + } + + /** + * set Choice Field option values + * + * @param integer $numFieldObj + * @param array $value + */ + public function setFormFieldOpt($numFieldObj, $value) + { + $this->o_field($numFieldObj, 'set', ['options' => $value]); + } + + /** + * add form to document + * + * @param integer $sigFlags + * @param boolean $needAppearances + */ + public function addForm($sigFlags = 0, $needAppearances = false) + { + $this->acroFormId = ++$this->numObj; + $this->o_acroform($this->acroFormId, 'new', [ + 'NeedAppearances' => $needAppearances ? 'true' : 'false', + 'SigFlags' => $sigFlags + ]); } /** @@ -3715,14 +4476,14 @@ EOT; { $y = $this->currentPageSize["height"] - $y; - $tm = array( + $tm = [ $s_x, 0, 0, $s_y, $x * (1 - $s_x), $y * (1 - $s_y) - ); + ]; $this->transform($tm); } @@ -3735,14 +4496,14 @@ EOT; */ function translate($t_x, $t_y) { - $tm = array( + $tm = [ 1, 0, 0, 1, $t_x, -$t_y - ); + ]; $this->transform($tm); } @@ -3762,14 +4523,14 @@ EOT; $cos_a = cos($a); $sin_a = sin($a); - $tm = array( + $tm = [ $cos_a, -$sin_a, $sin_a, $cos_a, $x - $sin_a * $y - $cos_a * $x, $y - $cos_a * $y + $sin_a * $x, - ); + ]; $this->transform($tm); } @@ -3789,14 +4550,14 @@ EOT; $tan_x = tan(deg2rad($angle_x)); $tan_y = tan(deg2rad($angle_y)); - $tm = array( + $tm = [ 1, -$tan_y, -$tan_x, 1, $tan_x * $y, $tan_y * $x, - ); + ]; $this->transform($tm); } @@ -3837,7 +4598,7 @@ EOT; // the id from the ezPdf class is the id of the contents of the page, not the page object itself // query that object to find the parent $rid = $this->objects[$id]['onPage']; - $opt = array('rid' => $rid, 'pos' => $pos); + $opt = ['rid' => $rid, 'pos' => $pos]; $this->o_page($this->numObj, 'new', $opt); } else { $this->o_page($this->numObj, 'new'); @@ -3874,7 +4635,7 @@ EOT; * @param string $filename The filename to present to the client. * @param array $options Associative array: 'compress' => 1 or 0 (default 1); 'Attachment' => 1 or 0 (default 1). */ - function stream($filename = "document.pdf", $options = array()) + function stream($filename = "document.pdf", $options = []) { if (headers_sent()) { die("Unable to stream pdf: headers already sent"); @@ -3890,7 +4651,7 @@ EOT; header("Content-Type: application/pdf"); header("Content-Length: " . mb_strlen($tmp, "8bit")); - $filename = str_replace(array("\n", "'"), "", basename($filename, ".pdf")) . ".pdf"; + $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf"; $attachment = $options["Attachment"] ? "attachment" : "inline"; $encoding = mb_detect_encoding($filename); @@ -4018,7 +4779,7 @@ EOT; } // the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290) - return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); + return strtr($text, [')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r']); } /** @@ -4039,8 +4800,8 @@ EOT; function utf8toCodePointsArray(&$text) { $length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040 - $unicode = array(); // array containing unicode values - $bytes = array(); // array containing single character byte sequences + $unicode = []; // array containing unicode values + $bytes = []; // array containing single character byte sequences $numbytes = 1; // number of octets needed to represent the UTF-8 character for ($i = 0; $i < $length; $i++) { @@ -4061,7 +4822,7 @@ EOT; } else { // use replacement character for other invalid sequences $unicode[] = 0xFFFD; - $bytes = array(); + $bytes = []; $numbytes = 1; } } elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN @@ -4072,7 +4833,7 @@ EOT; for ($j = 1; $j < $numbytes; $j++) { $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); } - if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) { + if ((($c >= 0xD800) and ($c <= 0xDFFF)) or ($c >= 0x10FFFF)) { // The definition of UTF-8 prohibits encoding character numbers between // U+D800 and U+DFFF, which are reserved for use with the UTF-16 // encoding form (as surrogate pairs) and do not directly represent @@ -4082,13 +4843,13 @@ EOT; $unicode[] = $c; // add char to array } // reset data for next char - $bytes = array(); + $bytes = []; $numbytes = 1; } } else { // use replacement character for other invalid sequences $unicode[] = 0xFFFD; - $bytes = array(); + $bytes = []; $numbytes = 1; } } @@ -4156,7 +4917,7 @@ EOT; $w += $wa * $nspaces; $a = deg2rad((float)$angle); - return array(cos($a) * $w + $x, -sin($a) * $w + $y); + return [cos($a) * $w + $x, -sin($a) * $w + $y]; } /** @@ -4194,7 +4955,7 @@ EOT; } if (!isset($this->stringSubsets[$font])) { - $this->stringSubsets[$font] = array(); + $this->stringSubsets[$font] = []; } $this->stringSubsets[$font] = array_unique( @@ -4220,25 +4981,25 @@ EOT; $this->selectFont($this->defaultFont); } - $text = str_replace(array("\r", "\n"), "", $text); + $text = str_replace(["\r", "\n"], "", $text); - if ($smallCaps) { - preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER); - $lower = $this->concatMatches($matches); - d($lower); + // if ($smallCaps) { + // preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER); + // $lower = $this->concatMatches($matches); + // d($lower); - preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER); - $other = $this->concatMatches($matches); - d($other); + // preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER); + // $other = $this->concatMatches($matches); + // d($other); - //$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text); - } + // $text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text); + // } // if there are any open callbacks, then they should be called, to show the start of the line if ($this->nCallback > 0) { for ($i = $this->nCallback; $i > 0; $i--) { // call each function - $info = array( + $info = [ 'x' => $x, 'y' => $y, 'angle' => $angle, @@ -4247,7 +5008,7 @@ EOT; 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'descender' => $this->callback[$i]['descender'] - ); + ]; $func = $this->callback[$i]['f']; $this->$func($info); @@ -4278,8 +5039,7 @@ EOT; $part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start); $place_text = $this->filterText($part, false); // modify unicode text so that extra word spacing is manually implemented (bug #) - $cf = $this->currentFont; - if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) { + if ($this->fonts[$this->currentFont]['isUnicode'] && $wordSpaceAdjust != 0) { $space_scale = 1000 / $size; $place_text = str_replace("\x00\x20", "\x00\x20)\x00\x20" . (-round($space_scale * $wordSpaceAdjust)) . "\x00\x20(", $place_text); } @@ -4302,7 +5062,7 @@ EOT; for ($i = $this->nCallback; $i > 0; $i--) { // call each function $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text); - $info = array( + $info = [ 'x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, @@ -4311,26 +5071,30 @@ EOT; 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'descender' => $this->callback[$i]['descender'] - ); + ]; $func = $this->callback[$i]['f']; $this->$func($info); } } + + if ($this->fonts[$this->currentFont]['isSubsetting']) { + $this->registerText($this->currentFont, $text); + } } /** * calculate how wide a given text string will be on a page, at a given size. * this can be called externally, but is also used by the other class functions * - * @param $size - * @param $text - * @param int $word_spacing - * @param int $char_spacing - * @return float|int + * @param float $size + * @param string $text + * @param float $word_spacing + * @param float $char_spacing + * @return float */ function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0) { - static $ord_cache = array(); + static $ord_cache = []; // this function should not change any of the settings, though it will need to // track any directives which change during calculation, so copy them at the start @@ -4341,7 +5105,7 @@ EOT; $this->selectFont($this->defaultFont); } - $text = str_replace(array("\r", "\n"), "", $text); + $text = str_replace(["\r", "\n"], "", $text); // converts a number or a float to a string so it can get the width $text = "$text"; @@ -4352,7 +5116,6 @@ EOT; $cf = $this->currentFont; $current_font = $this->fonts[$cf]; $space_scale = 1000 / ($size > 0 ? $size : 1); - $n_spaces = 0; if ($current_font['isUnicode']) { // for Unicode, use the code points array to calculate width rather @@ -4374,14 +5137,13 @@ EOT; // add additional padding for space if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space $w += $word_spacing * $space_scale; - $n_spaces++; } } } // add additional char spacing if ($char_spacing != 0) { - $w += $char_spacing * $space_scale * (count($unicode) + $n_spaces); + $w += $char_spacing * $space_scale * count($unicode); } } else { @@ -4410,14 +5172,13 @@ EOT; // add additional padding for space if (isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space') { // Space $w += $word_spacing * $space_scale; - $n_spaces++; } } } // add additional char spacing if ($char_spacing != 0) { - $w += $char_spacing * $space_scale * ($len + $n_spaces); + $w += $char_spacing * $space_scale * $len; } } @@ -4448,11 +5209,11 @@ EOT; // $this->currentLineStyle = $opt['lin']; } else { $this->nStateStack++; - $this->stateStack[$this->nStateStack] = array( + $this->stateStack[$this->nStateStack] = [ 'col' => $this->currentColor, 'str' => $this->currentStrokeColor, 'lin' => $this->currentLineStyle - ); + ]; } $this->save(); @@ -4490,7 +5251,7 @@ EOT; function openObject() { $this->nStack++; - $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage); + $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage]; // add a new object of the content type, to hold the data flow $this->numObj++; $this->o_contents($this->numObj, 'new'); @@ -4508,7 +5269,7 @@ EOT; function reopenObject($id) { $this->nStack++; - $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage); + $this->stack[$this->nStack] = ['c' => $this->currentContents, 'p' => $this->currentPage]; $this->currentContents = $id; // also if this object is the primary contents for a page, then set the current page to its parent @@ -4621,7 +5382,7 @@ EOT; } /** - * restore an object from its stored representation. returns its new object id. + * restore an object from its stored representation. Returns its new object id. * * @param $obj * @return int @@ -4635,6 +5396,27 @@ EOT; return $obj_id; } + /** + * Embeds a file inside the PDF + * + * @param string $filepath path to the file to store inside the PDF + * @param string $embeddedFilename the filename displayed in the list of embedded files + * @param string $description a description in the list of embedded files + */ + public function addEmbeddedFile(string $filepath, string $embeddedFilename, string $description): void + { + $this->numObj++; + $this->o_embedded_file_dictionary( + $this->numObj, + 'new', + [ + 'filepath' => $filepath, + 'filename' => $embeddedFilename, + 'description' => $description + ] + ); + } + /** * add content to the documents info object * @@ -4667,10 +5449,10 @@ EOT; // this will only work if the label is one of the valid ones. if (is_array($label)) { foreach ($label as $l => $v) { - $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v)); + $this->o_catalog($this->catalogId, 'viewerPreferences', [$l => $v]); } } else { - $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value)); + $this->o_catalog($this->catalogId, 'viewerPreferences', [$label => $value]); } } @@ -4698,7 +5480,7 @@ EOT; * Check if image already added to pdf image directory. * If yes, need not to create again (pass empty data) * - * @param $imgname + * @param string $imgname * @return bool */ function image_iscached($imgname) @@ -4710,17 +5492,17 @@ EOT; * add a PNG image into the document, from a GD object * this should work with remote files * + * @param \GdImage|resource $img A GD resource * @param string $file The PNG file * @param float $x X position * @param float $y Y position * @param float $w Width * @param float $h Height - * @param resource $img A GD resource * @param bool $is_mask true if the image is a mask * @param bool $mask true if the image is masked * @throws Exception */ - function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null) + function addImagePng(&$img, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null) { if (!function_exists("imagepng")) { throw new \Exception("The PHP GD extension is required, but is not installed."); @@ -4772,7 +5554,7 @@ EOT; } } //End isset($this->imagelist[$file]) (png Duplicate removal) - $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask); + $this->addPngFromBuf($data, $file, $x, $y, $w, $h, $is_mask, $mask); } /** @@ -4856,7 +5638,15 @@ EOT; // the first version containing it was 3.0.1RC1 static $imagickClonable = null; if ($imagickClonable === null) { - $imagickClonable = version_compare(Imagick::IMAGICK_EXTVER, '3.0.1rc1') > 0; + $imagickClonable = true; + if (defined('Imagick::IMAGICK_EXTVER')) { + $imagickVersion = \Imagick::IMAGICK_EXTVER; + } else { + $imagickVersion = '0'; + } + if (version_compare($imagickVersion, '0.0.1', '>=')) { + $imagickClonable = version_compare($imagickVersion, '3.0.1rc1', '>='); + } } $imagick = new \Imagick($file); @@ -4866,7 +5656,10 @@ EOT; if ($imagick->getImageAlphaChannel() !== 0) { $alpha_channel = $imagickClonable ? clone $imagick : $imagick->clone(); $alpha_channel->separateImageChannel(\Imagick::CHANNEL_ALPHA); - $alpha_channel->negateImage(true); + // Since ImageMagick7 negate invert transparency as default + if (\Imagick::getVersion()['versionNumber'] < 1800) { + $alpha_channel->negateImage(true); + } $alpha_channel->writeImage($tempfile_alpha); // Cast to 8bit+palette @@ -4889,7 +5682,7 @@ EOT; $imgplain = imagecreatefrompng($tempfile_plain); } else { // allocated colors cache - $allocated_colors = array(); + $allocated_colors = []; // extract alpha channel for ($xpx = 0; $xpx < $wpx; ++$xpx) { @@ -4901,7 +5694,7 @@ EOT; if ($eight_bit) { // with gamma correction $gammacorr = 2.2; - $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255; + $pixel = round(pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255); } else { // without gamma correction $pixel = (127 - $alpha) * 2; @@ -4931,21 +5724,19 @@ EOT; imagepng($imgplain, $tempfile_plain); } + $this->imageAlphaList[$file] = [$tempfile_alpha, $tempfile_plain]; + // embed mask image if ($tempfile_alpha) { - $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true); + $this->addImagePng($imgalpha, $tempfile_alpha, $x, $y, $w, $h, true); imagedestroy($imgalpha); + $this->imageCache[] = $tempfile_alpha; } // embed image, masked with previously embedded mask - $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, ($tempfile_alpha !== null)); + $this->addImagePng($imgplain, $tempfile_plain, $x, $y, $w, $h, false, ($tempfile_alpha !== null)); imagedestroy($imgplain); - - // remove temp files - if ($tempfile_alpha) { - unlink($tempfile_alpha); - } - unlink($tempfile_plain); + $this->imageCache[] = $tempfile_plain; } /** @@ -4965,6 +5756,19 @@ EOT; throw new \Exception("The PHP GD extension is required, but is not installed."); } + if (isset($this->imageAlphaList[$file])) { + [$alphaFile, $plainFile] = $this->imageAlphaList[$file]; + + if ($alphaFile) { + $img = null; + $this->addImagePng($img, $alphaFile, $x, $y, $w, $h, true); + } + + $img = null; + $this->addImagePng($img, $plainFile, $x, $y, $w, $h, false, ($plainFile !== null)); + return; + } + //if already cached, need not to read again if (isset($this->imagelist[$file])) { $img = null; @@ -4978,7 +5782,7 @@ EOT; // 3 => indexed // 4 => greyscale with alpha // 6 => fullcolor with alpha - $is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4); + $is_alpha = in_array($color_type, [4, 6]) || ($color_type == 3 && $bit_depth != 4); if ($is_alpha) { // exclude grayscale alpha $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type); @@ -5018,52 +5822,26 @@ EOT; imagecopy($img, $imgtmp, 0, 0, 0, 0, $sx, $sy); imagedestroy($imgtmp); } - $this->addImagePng($file, $x, $y, $w, $h, $img); + $this->addImagePng($img, $file, $x, $y, $w, $h); if ($img) { imagedestroy($img); } } - /** - * add a PNG image into the document, from a file - * this should work with remote files - * - * @param $file - * @param $x - * @param $y - * @param int $w - * @param int $h - */ - function addSvgFromFile($file, $x, $y, $w = 0, $h = 0) - { - $doc = new \Svg\Document(); - $doc->loadFile($file); - $dimensions = $doc->getDimensions(); - - $this->save(); - - $this->transform(array($w / $dimensions["width"], 0, 0, $h / $dimensions["height"], $x, $y)); - - $surface = new \Svg\Surface\SurfaceCpdf($doc, $this); - $doc->render($surface); - - $this->restore(); - } - /** * add a PNG image into the document, from a memory buffer of the file * + * @param $data * @param $file * @param $x * @param $y * @param float $w * @param float $h - * @param $data * @param bool $is_mask * @param null $mask */ - function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null) + function addPngFromBuf(&$data, $file, $x, $y, $w = 0.0, $h = 0.0, $is_mask = false, $mask = null) { if (isset($this->imagelist[$file])) { $data = null; @@ -5100,7 +5878,7 @@ EOT; // cycle through the file, identifying chunks $haveHeader = 0; - $info = array(); + $info = []; $idata = ''; $pdata = ''; @@ -5155,7 +5933,7 @@ EOT; case 'tRNS': //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk //print "tRNS found, color type = ".$info['colorType']."\n"; - $transparency = array(); + $transparency = []; switch ($info['colorType']) { // indexed color, rbg @@ -5301,7 +6079,7 @@ EOT; $this->numObj++; // $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width'])); - $options = array( + $options = [ 'label' => $label, 'data' => $idata, 'bitsPerComponent' => $info['bitDepth'], @@ -5313,14 +6091,14 @@ EOT; 'ncolor' => $ncolor, 'masked' => $mask, 'isMask' => $is_mask - ); + ]; if (isset($transparency)) { $options['transparency'] = $transparency; } $this->o_image($this->numObj, 'new', $options); - $this->imagelist[$file] = array('label' => $label, 'w' => $info['width'], 'h' => $info['height']); + $this->imagelist[$file] = ['label' => $label, 'w' => $info['width'], 'h' => $info['height']]; } if ($is_mask) { @@ -5392,31 +6170,31 @@ EOT; $h = $w * $imageHeight / $imageWidth; } - $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img); + $this->addJpegImage_common($data, $img, $imageWidth, $imageHeight, $x, $y, $w, $h, $channels); } /** * common code used by the two JPEG adding functions * @param $data + * @param $imgname + * @param $imageWidth + * @param $imageHeight * @param $x * @param $y * @param int $w * @param int $h - * @param $imageWidth - * @param $imageHeight * @param int $channels - * @param $imgname */ private function addJpegImage_common( &$data, + $imgname, + $imageWidth, + $imageHeight, $x, $y, $w = 0, $h = 0, - $imageWidth, - $imageHeight, - $channels = 3, - $imgname + $channels = 3 ) { if ($this->image_iscached($imgname)) { $label = $this->imagelist[$imgname]['label']; @@ -5440,21 +6218,21 @@ EOT; $this->o_image( $this->numObj, 'new', - array( + [ 'label' => $label, 'data' => &$data, 'iw' => $imageWidth, 'ih' => $imageHeight, 'channels' => $channels - ) + ] ); - $this->imagelist[$imgname] = array( + $this->imagelist[$imgname] = [ 'label' => $label, 'w' => $imageWidth, 'h' => $imageHeight, 'c' => $channels - ); + ]; } $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label)); @@ -5484,7 +6262,7 @@ EOT; $this->o_destination( $this->numObj, 'new', - array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c) + ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c] ); $id = $this->catalogId; $this->o_catalog($id, 'openHere', $this->numObj); @@ -5518,7 +6296,7 @@ EOT; $this->o_destination( $this->numObj, 'new', - array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c) + ['page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c] ); $id = $this->numObj; @@ -5542,28 +6320,28 @@ EOT; // these font families will be used to enable bold and italic markers to be included // within text streams. html forms will be used... $this->fontFamilies['Helvetica.afm'] = - array( + [ 'b' => 'Helvetica-Bold.afm', 'i' => 'Helvetica-Oblique.afm', 'bi' => 'Helvetica-BoldOblique.afm', 'ib' => 'Helvetica-BoldOblique.afm' - ); + ]; $this->fontFamilies['Courier.afm'] = - array( + [ 'b' => 'Courier-Bold.afm', 'i' => 'Courier-Oblique.afm', 'bi' => 'Courier-BoldOblique.afm', 'ib' => 'Courier-BoldOblique.afm' - ); + ]; $this->fontFamilies['Times-Roman.afm'] = - array( + [ 'b' => 'Times-Bold.afm', 'i' => 'Times-Italic.afm', 'bi' => 'Times-BoldItalic.afm', 'ib' => 'Times-BoldItalic.afm' - ); + ]; } } else { diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php similarity index 87% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php index fc8579735..62cc74a1d 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien M�nager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -15,7 +15,7 @@ class SurfaceCpdf implements SurfaceInterface { const DEBUG = false; - /** @var \CPdf\CPdf */ + /** @var \Svg\Surface\CPdf */ private $canvas; private $width; @@ -33,7 +33,7 @@ class SurfaceCpdf implements SurfaceInterface $h = $dimensions["height"]; if (!$canvas) { - $canvas = new \CPdf\CPdf(array(0, 0, $w, $h)); + $canvas = new \Svg\Surface\CPdf(array(0, 0, $w, $h)); $refl = new \ReflectionClass($canvas); $canvas->fontcache = realpath(dirname($refl->getFileName()) . "/../../fonts/")."/"; } @@ -122,10 +122,10 @@ class SurfaceCpdf implements SurfaceInterface $this->canvas->closePath(); } - public function fillStroke() + public function fillStroke(bool $close = false) { if (self::DEBUG) echo __FUNCTION__ . "\n"; - $this->canvas->fillStroke(); + $this->canvas->fillStroke($close); } public function clip() @@ -173,7 +173,7 @@ class SurfaceCpdf implements SurfaceInterface $data = file_get_contents($image); } - $image = tempnam("", "svg"); + $image = tempnam(sys_get_temp_dir(), "svg"); file_put_contents($image, $data); $img = $this->image($image, $sx, $sy, $sw, $sh, "normal"); @@ -342,10 +342,10 @@ class SurfaceCpdf implements SurfaceInterface $this->stroke(); } - public function stroke() + public function stroke(bool $close = false) { if (self::DEBUG) echo __FUNCTION__ . "\n"; - $this->canvas->stroke(); + $this->canvas->stroke($close); } public function endPath() @@ -415,11 +415,19 @@ class SurfaceCpdf implements SurfaceInterface $dashArray = preg_split('/\s*,\s*/', $style->strokeDasharray); } + + $phase=0; + if ($style->strokeDashoffset) { + $phase = $style->strokeDashoffset; + } + + $canvas->setLineStyle( $style->strokeWidth, $style->strokeLinecap, $style->strokeLinejoin, - $dashArray + $dashArray, + $phase ); $this->setFont($style->fontFamily, $style->fontStyle, $style->fontWeight); @@ -427,51 +435,52 @@ class SurfaceCpdf implements SurfaceInterface public function setFont($family, $style, $weight) { - $map = array( - "serif" => "Times", - "sans-serif" => "Helvetica", - "fantasy" => "Symbol", - "cursive" => "Times", - "monospace" => "Courier", + $map = [ + "serif" => "times", + "sans-serif" => "helvetica", + "fantasy" => "symbol", + "cursive" => "times", + "monospace" => "courier" + ]; - "arial" => "Helvetica", - "verdana" => "Helvetica", - ); + $styleMap = [ + "courier" => [ + "" => "Courier", + "b" => "Courier-Bold", + "i" => "Courier-Oblique", + "bi" => "Courier-BoldOblique", + ], + "helvetica" => [ + "" => "Helvetica", + "b" => "Helvetica-Bold", + "i" => "Helvetica-Oblique", + "bi" => "Helvetica-BoldOblique", + ], + "symbol" => [ + "" => "Symbol" + ], + "times" => [ + "" => "Times-Roman", + "b" => "Times-Bold", + "i" => "Times-Italic", + "bi" => "Times-BoldItalic", + ], + ]; - $styleMap = array( - 'Helvetica' => array( - 'b' => 'Helvetica-Bold', - 'i' => 'Helvetica-Oblique', - 'bi' => 'Helvetica-BoldOblique', - ), - 'Courier' => array( - 'b' => 'Courier-Bold', - 'i' => 'Courier-Oblique', - 'bi' => 'Courier-BoldOblique', - ), - 'Times' => array( - '' => 'Times-Roman', - 'b' => 'Times-Bold', - 'i' => 'Times-Italic', - 'bi' => 'Times-BoldItalic', - ), - ); - - $family = strtolower($family); - $style = strtolower($style); - $weight = strtolower($weight); - - if (isset($map[$family])) { - $family = $map[$family]; + $family_lc = strtolower($family); + if (isset($map[$family_lc])) { + $family = $map[$family_lc]; } if (isset($styleMap[$family])) { $key = ""; + $weight = strtolower($weight); if ($weight === "bold" || $weight === "bolder" || (is_numeric($weight) && $weight >= 600)) { $key .= "b"; } + $style = strtolower($style); if ($style === "italic" || $style === "oblique") { $key .= "i"; } diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceInterface.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceInterface.php similarity index 92% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceInterface.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceInterface.php index fb007edee..25b30014d 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceInterface.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfaceInterface.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien M�nager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -37,11 +37,11 @@ interface SurfaceInterface public function fill(); - public function stroke(); + public function stroke(bool $close = false); public function endPath(); - public function fillStroke(); + public function fillStroke(bool $close = false); public function clip(); @@ -87,4 +87,4 @@ interface SurfaceInterface public function getStyle(); public function setFont($family, $style, $weight); -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php similarity index 94% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php index a4d17342b..3d25aef81 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien M�nager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -120,10 +120,14 @@ class SurfacePDFLib implements SurfaceInterface $this->canvas->closepath(); } - public function fillStroke() + public function fillStroke(bool $close = false) { if (self::DEBUG) echo __FUNCTION__ . "\n"; - $this->canvas->fill_stroke(); + if ($close) { + $this->canvas->closepath_fill_stroke(); + } else { + $this->canvas->fill_stroke(); + } } public function clip() @@ -159,7 +163,7 @@ class SurfacePDFLib implements SurfaceInterface $data = file_get_contents($image); } - $image = tempnam("", "svg"); + $image = tempnam(sys_get_temp_dir(), "svg"); file_put_contents($image, $data); $img = $this->canvas->load_image("auto", $image, ""); @@ -281,10 +285,14 @@ class SurfacePDFLib implements SurfaceInterface $this->stroke(); } - public function stroke() + public function stroke(bool $close = false) { if (self::DEBUG) echo __FUNCTION__ . "\n"; - $this->canvas->stroke(); + if ($close) { + $this->canvas->closepath_stroke(); + } else { + $this->canvas->stroke(); + } } public function endPath() @@ -315,7 +323,7 @@ class SurfacePDFLib implements SurfaceInterface $this->style = $style; $canvas = $this->canvas; - if ($stroke = $style->stroke && is_array($style->stroke)) { + if (is_array($style->stroke) && $stroke = $style->stroke) { $canvas->setcolor( "stroke", "rgb", @@ -326,7 +334,7 @@ class SurfacePDFLib implements SurfaceInterface ); } - if ($fill = $style->fill && is_array($style->fill)) { + if (is_array($style->fill) && $fill = $style->fill) { $canvas->setcolor( "fill", "rgb", @@ -419,4 +427,4 @@ class SurfacePDFLib implements SurfaceInterface { // TODO: Implement setFont() method. } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/AbstractTag.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/AbstractTag.php similarity index 57% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/AbstractTag.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/AbstractTag.php index 7101ade83..9fa679320 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/AbstractTag.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/AbstractTag.php @@ -2,12 +2,13 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ namespace Svg\Tag; +use Svg\CssLength; use Svg\Document; use Svg\Style; @@ -134,21 +135,19 @@ abstract class AbstractTag $transform = $attributes["transform"]; - $match = array(); + $matches = array(); preg_match_all( - '/(matrix|translate|scale|rotate|skewX|skewY)\((.*?)\)/is', + '/(matrix|translate|scale|rotate|skew|skewX|skewY)\((.*?)\)/is', $transform, - $match, + $matches, PREG_SET_ORDER ); $transformations = array(); - if (count($match[0])) { - foreach ($match as $_match) { - $arguments = preg_split('/[ ,]+/', $_match[2]); - array_unshift($arguments, $_match[1]); - $transformations[] = $arguments; - } + foreach ($matches as $match) { + $arguments = preg_split('/[ ,]+/', $match[2]); + array_unshift($arguments, $match[1]); + $transformations[] = $arguments; } foreach ($transformations as $t) { @@ -166,18 +165,72 @@ abstract class AbstractTag break; case "rotate": - $surface->rotate($t[1]); + if (isset($t[2])) { + $t[3] = isset($t[3]) ? $t[3] : 0; + $surface->translate($t[2], $t[3]); + $surface->rotate($t[1]); + $surface->translate(-$t[2], -$t[3]); + } else { + $surface->rotate($t[1]); + } break; case "skewX": - $surface->skewX($t[1]); + $tan_x = tan(deg2rad($t[1])); + $surface->transform(1, 0, $tan_x, 1, 0, 0); break; case "skewY": - $surface->skewY($t[1]); + $tan_y = tan(deg2rad($t[1])); + $surface->transform(1, $tan_y, 0, 1, 0, 0); break; } } } } + + /** + * Convert the given size for the context of this current tag. + * Takes a pixel-based reference, which is usually specific to the context of the size, + * but the actual reference size will be decided based upon the unit used. + * + * @param string $size + * @param float $pxReference + * + * @return float + */ + protected function convertSize(string $size, float $pxReference): float + { + $length = new CssLength($size); + $reference = $pxReference; + $defaultFontSize = 12; + + switch ($length->getUnit()) { + case "em": + $reference = $this->style->fontSize ?? $defaultFontSize; + break; + case "rem": + $reference = $this->document->style->fontSize ?? $defaultFontSize; + break; + case "ex": + case "ch": + $emRef = $this->style->fontSize ?? $defaultFontSize; + $reference = $emRef * 0.5; + break; + case "vw": + $reference = $this->getDocument()->getWidth(); + break; + case "vh": + $reference = $this->getDocument()->getHeight(); + break; + case "vmin": + $reference = min($this->getDocument()->getHeight(), $this->getDocument()->getWidth()); + break; + case "vmax": + $reference = max($this->getDocument()->getHeight(), $this->getDocument()->getWidth()); + break; + } + + return (new CssLength($size))->toPixels($reference); + } } diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Anchor.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Anchor.php similarity index 77% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Anchor.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Anchor.php index 9a4b3fe21..6979495c1 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Anchor.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Anchor.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -11,4 +11,4 @@ namespace Svg\Tag; class Anchor extends Group { -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Circle.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Circle.php similarity index 54% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Circle.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Circle.php index 2e516b408..e504ffe3a 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Circle.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Circle.php @@ -2,12 +2,14 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ namespace Svg\Tag; +use Svg\Style; + class Circle extends Shape { protected $cx = 0; @@ -17,15 +19,18 @@ class Circle extends Shape public function start($attributes) { if (isset($attributes['cx'])) { - $this->cx = $attributes['cx']; + $width = $this->document->getWidth(); + $this->cx = $this->convertSize($attributes['cx'], $width); } if (isset($attributes['cy'])) { - $this->cy = $attributes['cy']; + $height = $this->document->getHeight(); + $this->cy = $this->convertSize($attributes['cy'], $height); } if (isset($attributes['r'])) { - $this->r = $attributes['r']; + $diagonal = $this->document->getDiagonal(); + $this->r = $this->convertSize($attributes['r'], $diagonal); } $this->document->getSurface()->circle($this->cx, $this->cy, $this->r); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/ClipPath.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/ClipPath.php similarity index 91% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/ClipPath.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/ClipPath.php index 54ee084c4..46722f934 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/ClipPath.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/ClipPath.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -30,4 +30,4 @@ class ClipPath extends AbstractTag { $this->document->getSurface()->restore(); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Ellipse.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Ellipse.php similarity index 60% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Ellipse.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Ellipse.php index 483e51e74..42891e031 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Ellipse.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Ellipse.php @@ -2,12 +2,14 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ namespace Svg\Tag; +use Svg\Style; + class Ellipse extends Shape { protected $cx = 0; @@ -19,19 +21,22 @@ class Ellipse extends Shape { parent::start($attributes); + $width = $this->document->getWidth(); + $height = $this->document->getHeight(); + if (isset($attributes['cx'])) { - $this->cx = $attributes['cx']; + $this->cx = $this->convertSize($attributes['cx'], $width); } if (isset($attributes['cy'])) { - $this->cy = $attributes['cy']; + $this->cy = $this->convertSize($attributes['cy'], $height); } if (isset($attributes['rx'])) { - $this->rx = $attributes['rx']; + $this->rx = $this->convertSize($attributes['rx'], $width); } if (isset($attributes['ry'])) { - $this->ry = $attributes['ry']; + $this->ry = $this->convertSize($attributes['ry'], $height); } $this->document->getSurface()->ellipse($this->cx, $this->cy, $this->rx, $this->ry, 0, 0, 360, false); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Group.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Group.php similarity index 91% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Group.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Group.php index 542bfbdbd..bacb3851c 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Group.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Group.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -30,4 +30,4 @@ class Group extends AbstractTag { $this->document->getSurface()->restore(); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Image.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Image.php similarity index 60% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Image.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Image.php index f356b28de..bda17ea31 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Image.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Image.php @@ -2,12 +2,14 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ namespace Svg\Tag; +use Svg\Style; + class Image extends AbstractTag { protected $x = 0; @@ -28,35 +30,39 @@ class Image extends AbstractTag public function start($attributes) { - $document = $this->document; $height = $this->document->getHeight(); + $width = $this->document->getWidth(); $this->y = $height; if (isset($attributes['x'])) { - $this->x = $attributes['x']; + $this->x = $this->convertSize($attributes['x'], $width); } if (isset($attributes['y'])) { - $this->y = $height - $attributes['y']; + $this->y = $height - $this->convertSize($attributes['y'], $height); } if (isset($attributes['width'])) { - $this->width = $attributes['width']; + $this->width = $this->convertSize($attributes['width'], $width); } if (isset($attributes['height'])) { - $this->height = $attributes['height']; + $this->height = $this->convertSize($attributes['height'], $height); } if (isset($attributes['xlink:href'])) { $this->href = $attributes['xlink:href']; } - $document->getSurface()->transform(1, 0, 0, -1, 0, $height); + if (isset($attributes['href'])) { + $this->href = $attributes['href']; + } - $document->getSurface()->drawImage($this->href, $this->x, $this->y, $this->width, $this->height); + $this->document->getSurface()->transform(1, 0, 0, -1, 0, $height); + + $this->document->getSurface()->drawImage($this->href, $this->x, $this->y, $this->width, $this->height); } protected function after() { $this->document->getSurface()->restore(); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Line.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Line.php similarity index 60% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Line.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Line.php index 42504bca5..fb3b64c48 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Line.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Line.php @@ -2,12 +2,14 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ namespace Svg\Tag; +use Svg\Style; + class Line extends Shape { protected $x1 = 0; @@ -18,21 +20,24 @@ class Line extends Shape public function start($attributes) { + $height = $this->document->getHeight(); + $width = $this->document->getWidth(); + if (isset($attributes['x1'])) { - $this->x1 = $attributes['x1']; + $this->x1 = $this->convertSize($attributes['x1'], $width); } if (isset($attributes['y1'])) { - $this->y1 = $attributes['y1']; + $this->y1 = $this->convertSize($attributes['y1'], $height); } if (isset($attributes['x2'])) { - $this->x2 = $attributes['x2']; + $this->x2 = $this->convertSize($attributes['x2'], $width); } if (isset($attributes['y2'])) { - $this->y2 = $attributes['y2']; + $this->y2 = $this->convertSize($attributes['y2'], $height); } $surface = $this->document->getSurface(); $surface->moveTo($this->x1, $this->y1); $surface->lineTo($this->x2, $this->y2); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/LinearGradient.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/LinearGradient.php similarity index 97% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/LinearGradient.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/LinearGradient.php index 605ec23d9..c5e63979e 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/LinearGradient.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/LinearGradient.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -80,4 +80,4 @@ class LinearGradient extends AbstractTag return $this->stops; } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Path.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Path.php similarity index 86% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Path.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Path.php index c43d6388c..3dce7a619 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Path.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Path.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -12,6 +12,27 @@ use Svg\Surface\SurfaceInterface; class Path extends Shape { + // kindly borrowed from fabric.util.parsePath. + /* @see https://github.com/fabricjs/fabric.js/blob/master/src/util/path.js#L664 */ + const NUMBER_PATTERN = '([-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?)\s*'; + const COMMA_PATTERN = '(?:\s+,?\s*|,\s*)?'; + const FLAG_PATTERN = '([01])'; + const ARC_REGEXP = '/' + . self::NUMBER_PATTERN + . self::COMMA_PATTERN + . self::NUMBER_PATTERN + . self::COMMA_PATTERN + . self::NUMBER_PATTERN + . self::COMMA_PATTERN + . self::FLAG_PATTERN + . self::COMMA_PATTERN + . self::FLAG_PATTERN + . self::COMMA_PATTERN + . self::NUMBER_PATTERN + . self::COMMA_PATTERN + . self::NUMBER_PATTERN + . '/'; + static $commandLengths = array( 'm' => 2, 'l' => 2, @@ -29,24 +50,37 @@ class Path extends Shape 'M' => 'L', ); - public function start($attributes) + public static function parse(string $commandSequence): array { - if (!isset($attributes['d'])) { - $this->hasShape = false; - - return; - } - $commands = array(); - preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $attributes['d'], $commands, PREG_SET_ORDER); - + preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $commandSequence, $commands, PREG_SET_ORDER); + $path = array(); foreach ($commands as $c) { if (count($c) == 3) { + $commandLower = strtolower($c[1]); + + // arcs have special flags that apparently don't require spaces. + if ($commandLower === 'a' && preg_match_all(static::ARC_REGEXP, $c[2], $matches, PREG_PATTERN_ORDER)) { + $numberOfMatches = count($matches[0]); + for ($k = 0; $k < $numberOfMatches; ++$k) { + $path[] = [ + $c[1], + $matches[1][$k], + $matches[2][$k], + $matches[3][$k], + $matches[4][$k], + $matches[5][$k], + $matches[6][$k], + $matches[7][$k], + ]; + } + continue; + } + $arguments = array(); preg_match_all('/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/i', $c[2], $arguments, PREG_PATTERN_ORDER); $item = $arguments[0]; - $commandLower = strtolower($c[1]); if ( isset(self::$commandLengths[$commandLower]) && @@ -75,6 +109,18 @@ class Path extends Shape } } + return $path; + } + + public function start($attributes) + { + if (!isset($attributes['d'])) { + $this->hasShape = false; + + return; + } + + $path = static::parse($attributes['d']); $surface = $this->document->getSurface(); // From https://github.com/kangax/fabric.js/blob/master/src/shapes/path.class.js @@ -92,7 +138,6 @@ class Path extends Shape $tempControlY = null; $l = 0; //-((this.width / 2) + $this.pathOffset.x), $t = 0; //-((this.height / 2) + $this.pathOffset.y), - $methodName = null; foreach ($path as $current) { switch ($current[0]) { // first letter @@ -287,23 +332,16 @@ class Path extends Shape $tempX = $x + $current[1]; $tempY = $y + $current[2]; - if (preg_match("/[QqTt]/", $previous[0])) { - // If there is no previous command or if the previous command was not a Q, q, T or t, - // assume the control point is coincident with the current point + // calculate reflection of previous control points + if (preg_match('/[QqT]/', $previous[0])) { + $controlX = 2 * $x - $controlX; + $controlY = 2 * $y - $controlY; + } elseif ($previous[0] === 't') { + $controlX = 2 * $x - $tempControlX; + $controlY = 2 * $y - $tempControlY; + } else { $controlX = $x; $controlY = $y; - } else { - if ($previous[0] === 't') { - // calculate reflection of previous control points for t - $controlX = 2 * $x - $tempControlX; - $controlY = 2 * $y - $tempControlY; - } else { - if ($previous[0] === 'q') { - // calculate reflection of previous control points for q - $controlX = 2 * $x - $controlX; - $controlY = 2 * $y - $controlY; - } - } } $tempControlX = $controlX; @@ -317,8 +355,6 @@ class Path extends Shape ); $x = $tempX; $y = $tempY; - $controlX = $x + $current[1]; - $controlY = $y + $current[2]; break; case 'T': @@ -326,8 +362,14 @@ class Path extends Shape $tempY = $current[2]; // calculate reflection of previous control points - $controlX = 2 * $x - $controlX; - $controlY = 2 * $y - $controlY; + if (preg_match('/[QqTt]/', $previous[0])) { + $controlX = 2 * $x - $controlX; + $controlY = 2 * $y - $controlY; + } else { + $controlX = $x; + $controlY = $y; + } + $surface->quadraticCurveTo( $controlX + $l, $controlY + $t, @@ -339,7 +381,6 @@ class Path extends Shape break; case 'a': - // TODO: optimize this $this->drawArc( $surface, $x + $l, @@ -405,7 +446,14 @@ class Path extends Shape array(), ); - $segsNorm = $this->arcToSegments($tx - $fx, $ty - $fy, $rx, $ry, $large, $sweep, $rot); + $toX = $tx - $fx; + $toY = $ty - $fy; + + if ($toX + $toY === 0) { + return; + } + + $segsNorm = $this->arcToSegments($toX, $toY, $rx, $ry, $large, $sweep, $rot); for ($i = 0, $len = count($segsNorm); $i < $len; $i++) { $segs[$i][0] = $segsNorm[$i][0] + $fx; @@ -525,4 +573,4 @@ class Path extends Shape return 2 * M_PI - ($ta - $tb); } } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polygon.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polygon.php similarity index 70% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polygon.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polygon.php index 3100c5e08..e7ca92a1b 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polygon.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polygon.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -13,16 +13,25 @@ class Polygon extends Shape public function start($attributes) { $tmp = array(); - preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp); + preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp, PREG_PATTERN_ORDER); $points = $tmp[0]; $count = count($points); + if ($count < 4) { + // nothing to draw + return; + } + $surface = $this->document->getSurface(); list($x, $y) = $points; $surface->moveTo($x, $y); for ($i = 2; $i < $count; $i += 2) { + if ($i + 1 === $count) { + // invalid trailing point + continue; + } $x = $points[$i]; $y = $points[$i + 1]; $surface->lineTo($x, $y); @@ -30,4 +39,4 @@ class Polygon extends Shape $surface->closePath(); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polyline.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polyline.php similarity index 69% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polyline.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polyline.php index c2837f586..45e2131ec 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polyline.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Polyline.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -13,19 +13,28 @@ class Polyline extends Shape public function start($attributes) { $tmp = array(); - preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp); + preg_match_all('/([\-]*[0-9\.]+)/', $attributes['points'], $tmp, PREG_PATTERN_ORDER); $points = $tmp[0]; $count = count($points); + if ($count < 4) { + // nothing to draw + return; + } + $surface = $this->document->getSurface(); list($x, $y) = $points; $surface->moveTo($x, $y); for ($i = 2; $i < $count; $i += 2) { + if ($i + 1 === $count) { + // invalid trailing point + continue; + } $x = $points[$i]; $y = $points[$i + 1]; $surface->lineTo($x, $y); } } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/RadialGradient.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/RadialGradient.php similarity index 82% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/RadialGradient.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/RadialGradient.php index 93987b393..a9de62f31 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/RadialGradient.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/RadialGradient.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -14,4 +14,4 @@ class RadialGradient extends AbstractTag { } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Rect.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Rect.php similarity index 66% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Rect.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Rect.php index 7d32127f6..b5f3f774b 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Rect.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Rect.php @@ -2,12 +2,14 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ namespace Svg\Tag; +use Svg\Style; + class Rect extends Shape { protected $x = 0; @@ -19,18 +21,21 @@ class Rect extends Shape public function start($attributes) { + $width = $this->document->getWidth(); + $height = $this->document->getHeight(); + if (isset($attributes['x'])) { - $this->x = $attributes['x']; + $this->x = $this->convertSize($attributes['x'], $width); } if (isset($attributes['y'])) { - $this->y = $attributes['y']; + $this->y = $this->convertSize($attributes['y'], $height); } if (isset($attributes['width'])) { - $this->width = $attributes['width']; + $this->width = $this->convertSize($attributes['width'], $width); } if (isset($attributes['height'])) { - $this->height = $attributes['height']; + $this->height = $this->convertSize($attributes['height'], $height); } if (isset($attributes['rx'])) { @@ -42,4 +47,4 @@ class Rect extends Shape $this->document->getSurface()->rect($this->x, $this->y, $this->width, $this->height, $this->rx, $this->ry); } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Shape.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Shape.php similarity index 94% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Shape.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Shape.php index 0a2bfaef5..767e81dbf 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Shape.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Shape.php @@ -38,7 +38,7 @@ class Shape extends AbstractTag if ($fill) { if ($stroke) { - $surface->fillStroke(); + $surface->fillStroke(false); } else { // if (is_string($style->fill)) { // /** @var LinearGradient|RadialGradient $gradient */ @@ -51,7 +51,7 @@ class Shape extends AbstractTag } } elseif ($stroke) { - $surface->stroke(); + $surface->stroke(false); } else { $surface->endPath(); diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Stop.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Stop.php similarity index 81% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Stop.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Stop.php index 666a2ac71..22c9a9888 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Stop.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Stop.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -14,4 +14,4 @@ class Stop extends AbstractTag { } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/StyleTag.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/StyleTag.php similarity index 89% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/StyleTag.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/StyleTag.php index cda549346..309de01ef 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/StyleTag.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/StyleTag.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien Mnager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -24,4 +24,4 @@ class StyleTag extends AbstractTag { $this->text .= $text; } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Text.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Text.php similarity index 83% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Text.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Text.php index 83b5afe6a..80e08a600 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Text.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/Text.php @@ -8,6 +8,8 @@ namespace Svg\Tag; +use Svg\Style; + class Text extends Shape { protected $x = 0; @@ -16,18 +18,18 @@ class Text extends Shape public function start($attributes) { - $document = $this->document; $height = $this->document->getHeight(); $this->y = $height; if (isset($attributes['x'])) { - $this->x = $attributes['x']; + $width = $this->document->getWidth(); + $this->x = $this->convertSize($attributes['x'], $width); } if (isset($attributes['y'])) { - $this->y = $height - $attributes['y']; + $this->y = $height - $this->convertSize($attributes['y'], $height); } - $document->getSurface()->transform(1, 0, 0, -1, 0, $height); + $this->document->getSurface()->transform(1, 0, 0, -1, 0, $height); } public function end() diff --git a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/UseTag.php b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/UseTag.php similarity index 75% rename from library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/UseTag.php rename to library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/UseTag.php index b88a6cce1..c5f00ea9d 100644 --- a/library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/UseTag.php +++ b/library/vendor/dompdf/vendor/phenx/php-svg-lib/src/Svg/Tag/UseTag.php @@ -2,7 +2,7 @@ /** * @package php-svg-lib * @link http://github.com/PhenX/php-svg-lib - * @author Fabien M�nager + * @author Fabien Ménager * @license GNU LGPLv3+ http://www.gnu.org/copyleft/lesser.html */ @@ -38,7 +38,7 @@ class UseTag extends AbstractTag $document = $this->getDocument(); - $link = $attributes["xlink:href"]; + $link = $attributes["href"] ?? $attributes["xlink:href"]; $this->reference = $document->getDef($link); if ($this->reference) { @@ -69,12 +69,18 @@ class UseTag extends AbstractTag return; } - $attributes = array_merge($this->reference->attributes, $attributes); + $mergedAttributes = $this->reference->attributes; + $attributesToNotMerge = ['x', 'y', 'width', 'height']; + foreach ($attributes as $attrKey => $attrVal) { + if (!in_array($attrKey, $attributesToNotMerge) && !isset($mergedAttributes[$attrKey])) { + $mergedAttributes[$attrKey] = $attrVal; + } + } - $this->reference->handle($attributes); + $this->reference->handle($mergedAttributes); foreach ($this->reference->children as $_child) { - $_attributes = array_merge($_child->attributes, $attributes); + $_attributes = array_merge($_child->attributes, $mergedAttributes); $_child->handle($_attributes); } } @@ -93,4 +99,4 @@ class UseTag extends AbstractTag $_child->handleEnd(); } } -} \ No newline at end of file +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/CHANGELOG.md b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/CHANGELOG.md new file mode 100644 index 000000000..a23fd0e68 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/CHANGELOG.md @@ -0,0 +1,241 @@ +# Revision History + +## 8.4.0 + +### Features + +* Support for PHP 8.x +* PHPDoc annotations +* Allow usage of CSS variables inside color functions (by parsing them as regular functions) +* Use PSR-12 code style +* *No deprecations* + +### Bugfixes + +* Improved handling of whitespace in `calc()` +* Fix parsing units whose prefix is also a valid unit, like `vmin` +* Allow passing an object to `CSSList#replace` +* Fix PHP 7.3 warnings +* Correctly parse keyframes with `%` +* Don’t convert large numbers to scientific notation +* Allow a file to end after an `@import` +* Preserve case of CSS variables as specced +* Allow identifiers to use escapes the same way as strings +* No longer use `eval` for the comparison in `getSelectorsBySpecificity`, in case it gets passed untrusted input (CVE-2020-13756). Also fixed in 8.3.1, 8.2.1, 8.1.1, 8.0.1, 7.0.4, 6.0.2, 5.2.1, 5.1.3, 5.0.9, 4.0.1, 3.0.1, 2.0.1, 1.0.1. +* Prevent an infinite loop when parsing invalid grid line names +* Remove invalid unit `vm` +* Retain rule order after expanding shorthands + +### Backwards-incompatible changes + +* PHP ≥ 5.6 is now required +* HHVM compatibility target dropped + +## 8.3.0 (2019-02-22) + +* Refactor parsing logic to mostly reside in the class files whose data structure is to be parsed (this should eventually allow us to unit-test specific parts of the parsing logic individually). +* Fix error in parsing `calc` expessions when the first operand is a negative number, thanks to @raxbg. +* Support parsing CSS4 colors in hex notation with alpha values, thanks to @raxbg. +* Swallow more errors in lenient mode, thanks to @raxbg. +* Allow specifying arbitrary strings to output before and after declaration blocks, thanks to @westonruter. +* *No backwards-incompatible changes* +* *No deprecations* + +## 8.2.0 (2018-07-13) + +* Support parsing `calc()`, thanks to @raxbg. +* Support parsing grid-lines, again thanks to @raxbg. +* Support parsing legacy IE filters (`progid:`) in lenient mode, thanks to @FMCorz +* Performance improvements parsing large files, again thanks to @FMCorz +* *No backwards-incompatible changes* +* *No deprecations* + +## 8.1.0 (2016-07-19) + +* Comments are no longer silently ignored but stored with the object with which they appear (no render support, though). Thanks to @FMCorz. +* The IE hacks using `\0` and `\9` can now be parsed (and rendered) in lenient mode. Thanks (again) to @FMCorz. +* Media queries with or without spaces before the query are parsed. Still no *real* parsing support, though. Sorry… +* PHPUnit is now listed as a dev-dependency in composer.json. +* *No backwards-incompatible changes* +* *No deprecations* + +## 8.0.0 (2016-06-30) + +* Store source CSS line numbers in tokens and parsing exceptions. +* *No deprecations* + +### Backwards-incompatible changes + +* Unrecoverable parser errors throw an exception of type `Sabberworm\CSS\Parsing\SourceException` instead of `\Exception`. + +## 7.0.3 (2016-04-27) + +* Fixed parsing empty CSS when multibyte is off +* *No backwards-incompatible changes* +* *No deprecations* + +## 7.0.2 (2016-02-11) + +* 150 time performance boost thanks to @[ossinkine](https://github.com/ossinkine) +* *No backwards-incompatible changes* +* *No deprecations* + +## 7.0.1 (2015-12-25) + +* No more suppressed `E_NOTICE` +* *No backwards-incompatible changes* +* *No deprecations* + +## 7.0.0 (2015-08-24) + +* Compatibility with PHP 7. Well timed, eh? +* *No deprecations* + +### Backwards-incompatible changes + +* The `Sabberworm\CSS\Value\String` class has been renamed to `Sabberworm\CSS\Value\CSSString`. + +## 6.0.1 (2015-08-24) + +* Remove some declarations in interfaces incompatible with PHP 5.3 (< 5.3.9) +* *No deprecations* + +## 6.0.0 (2014-07-03) + +* Format output using Sabberworm\CSS\OutputFormat +* *No backwards-incompatible changes* + +### Deprecations + +* The parse() method replaces __toString with an optional argument (instance of the OutputFormat class) + +## 5.2.0 (2014-06-30) + +* Support removing a selector from a declaration block using `$oBlock->removeSelector($mSelector)` +* Introduce a specialized exception (Sabberworm\CSS\Parsing\OuputException) for exceptions during output rendering + +* *No deprecations* + +#### Backwards-incompatible changes + +* Outputting a declaration block that has no selectors throws an OuputException instead of outputting an invalid ` {…}` into the CSS document. + +## 5.1.2 (2013-10-30) + +* Remove the use of consumeUntil in comment parsing. This makes it possible to parse comments such as `/** Perfectly valid **/` +* Add fr relative size unit +* Fix some issues with HHVM +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.1.1 (2013-10-28) + +* Updated CHANGELOG.md to reflect changes since 5.0.4 +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.1.0 (2013-10-24) + +* Performance enhancements by Michael M Slusarz +* More rescue entry points for lenient parsing (unexpected tokens between declaration blocks and unclosed comments) +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.8 (2013-08-15) + +* Make default settings’ multibyte parsing option dependent on whether or not the mbstring extension is actually installed. +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.7 (2013-08-04) + +* Fix broken decimal point output optimization +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.6 (2013-05-31) + +* Fix broken unit test +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.5 (2013-04-17) + +* Initial support for lenient parsing (setting this parser option will catch some exceptions internally and recover the parser’s state as neatly as possible). +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.4 (2013-03-21) + +* Don’t output floats with locale-aware separator chars +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.3 (2013-03-21) + +* More size units recognized +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.2 (2013-03-21) + +* CHANGELOG.md file added to distribution +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.1 (2013-03-20) + +* Internal cleanup +* *No backwards-incompatible changes* +* *No deprecations* + +## 5.0.0 (2013-03-20) + +* Correctly parse all known CSS 3 units (including Hz and kHz). +* Output RGB colors in short (#aaa or #ababab) notation +* Be case-insensitive when parsing identifiers. +* *No deprecations* + +### Backwards-incompatible changes + +* `Sabberworm\CSS\Value\Color`’s `__toString` method overrides `CSSList`’s to maybe return something other than `type(value, …)` (see above). + +## 4.0.0 (2013-03-19) + +* Support for more @-rules +* Generic interface `Sabberworm\CSS\Property\AtRule`, implemented by all @-rule classes +* *No deprecations* + +### Backwards-incompatible changes + +* `Sabberworm\CSS\RuleSet\AtRule` renamed to `Sabberworm\CSS\RuleSet\AtRuleSet` +* `Sabberworm\CSS\CSSList\MediaQuery` renamed to `Sabberworm\CSS\RuleSet\CSSList\AtRuleBlockList` with differing semantics and API (which also works for other block-list-based @-rules like `@supports`). + +## 3.0.0 (2013-03-06) + +* Support for lenient parsing (on by default) +* *No deprecations* + +### Backwards-incompatible changes + +* All properties (like whether or not to use `mb_`-functions, which default charset to use and – new – whether or not to be forgiving when parsing) are now encapsulated in an instance of `Sabberworm\CSS\Settings` which can be passed as the second argument to `Sabberworm\CSS\Parser->__construct()`. +* Specifying a charset as the second argument to `Sabberworm\CSS\Parser->__construct()` is no longer supported. Use `Sabberworm\CSS\Settings::create()->withDefaultCharset('some-charset')` instead. +* Setting `Sabberworm\CSS\Parser->bUseMbFunctions` has no effect. Use `Sabberworm\CSS\Settings::create()->withMultibyteSupport(true/false)` instead. +* `Sabberworm\CSS\Parser->parse()` may throw a `Sabberworm\CSS\Parsing\UnexpectedTokenException` when in strict parsing mode. + +## 2.0.0 (2013-01-29) + +* Allow multiple rules of the same type per rule set + +### Backwards-incompatible changes + +* `Sabberworm\CSS\RuleSet->getRules()` returns an index-based array instead of an associative array. Use `Sabberworm\CSS\RuleSet->getRulesAssoc()` (which eliminates duplicate rules and lets the later rule of the same name win). +* `Sabberworm\CSS\RuleSet->removeRule()` works as it did before except when passed an instance of `Sabberworm\CSS\Rule\Rule`, in which case it would only remove the exact rule given instead of all the rules of the same type. To get the old behaviour, use `Sabberworm\CSS\RuleSet->removeRule($oRule->getRule()`; + +## 1.0 + +Initial release of a stable public API. + +## 0.9 + +Last version not to use PSR-0 project organization semantics. diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/LICENSE b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/LICENSE new file mode 100644 index 000000000..686a4e311 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2011 Raphael Schweikert, https://www.sabberworm.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/README.md b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/README.md new file mode 100644 index 000000000..66fb1c655 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/README.md @@ -0,0 +1,632 @@ +# PHP CSS Parser + +[![Build Status](https://github.com/sabberworm/PHP-CSS-Parser/workflows/CI/badge.svg?branch=master)](https://github.com/sabberworm/PHP-CSS-Parser/actions/) + +A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output as (optimized) CSS. + +## Usage + +### Installation using Composer + +```bash +composer require sabberworm/php-css-parser +``` + +### Extraction + +To use the CSS Parser, create a new instance. The constructor takes the following form: + +```php +new \Sabberworm\CSS\Parser($css); +``` + +To read a file, for example, you’d do the following: + +```php +$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); +$cssDocument = $parser->parse(); +``` + +The resulting CSS document structure can be manipulated prior to being output. + +### Options + +#### Charset + +The charset option is used only if no `@charset` declaration is found in the CSS file. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that. + +```php +$settings = \Sabberworm\CSS\Settings::create() + ->withDefaultCharset('windows-1252'); +$parser = new \Sabberworm\CSS\Parser($css, $settings); +``` + +#### Strict parsing + +To have the parser choke on invalid rules, supply a thusly configured `\Sabberworm\CSS\Settings` object: + +```php +$parser = new \Sabberworm\CSS\Parser( + file_get_contents('somefile.css'), + \Sabberworm\CSS\Settings::create()->beStrict() +); +``` + +#### Disable multibyte functions + +To achieve faster parsing, you can choose to have PHP-CSS-Parser use regular string functions instead of `mb_*` functions. This should work fine in most cases, even for UTF-8 files, as all the multibyte characters are in string literals. Still it’s not recommended using this with input you have no control over as it’s not thoroughly covered by test cases. + +```php +$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false); +$parser = new \Sabberworm\CSS\Parser($css, $settings); +``` + +### Manipulation + +The resulting data structure consists mainly of five basic types: `CSSList`, `RuleSet`, `Rule`, `Selector` and `Value`. There are two additional types used: `Import` and `Charset`, which you won’t use often. + +#### CSSList + +`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc. `CSSList` has the following concrete subtypes: + +* `Document` – representing the root of a CSS file. +* `MediaQuery` – represents a subsection of a `CSSList` that only applies to an output device matching the contained media query. + +To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use instanceof to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`. + +To append a new item (selector, media query, etc.) to an existing `CSSList`, construct it using the constructor for this class and use the `append($oItem)` method. + +#### RuleSet + +`RuleSet` is a container for individual rules. The most common form of a rule set is one constrained by a selector. The following concrete subtypes exist: + +* `AtRuleSet` – for generic at-rules which do not match the ones specifically mentioned like `@import`, `@charset` or `@media`. A common example for this is `@font-face`. +* `DeclarationBlock` – a `RuleSet` constrained by a `Selector`; contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the matching elements. + +Note: A `CSSList` can contain other `CSSList`s (and `Import`s as well as a `Charset`), while a `RuleSet` can only contain `Rule`s. + +If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` instance or a rule name; optionally suffixed by a dash to remove all related rules). + +#### Rule + +`Rule`s just have a key (the rule) and a value. These values are all instances of a `Value`. + +#### Value + +`Value` is an abstract class that only defines the `render` method. The concrete subclasses for atomic value types are: + +* `Size` – consists of a numeric `size` value and a unit. +* `Color` – colors can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form. +* `CSSString` – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes. +* `URL` – URLs in CSS; always output in URL("") notation. + +There is another abstract subclass of `Value`, `ValueList`. A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`). There are two types of `ValueList`s: + +* `RuleValueList` – The default type, used to represent all multi-valued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list). +* `CSSFunction` – A special kind of value that also contains a function name and where the values are the function’s arguments. Also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`. + +#### Convenience methods + +There are a few convenience methods on Document to ease finding, manipulating and deleting rules: + +* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested your selectors are. Aliased as `getAllSelectors()`. +* `getAllRuleSets()` – does what it says; no matter how deeply nested your rule sets are. +* `getAllValues()` – finds all `Value` objects inside `Rule`s. + +## To-Do + +* More convenience methods (like `selectorsWithElement($sId/Class/TagName)`, `attributesOfType($type)`, `removeAttributesOfType($type)`) +* Real multibyte support. Currently, only multibyte charsets whose first 255 code points take up only one byte and are identical with ASCII are supported (yes, UTF-8 fits this description). +* Named color support (using `Color` instead of an anonymous string literal) + +## Use cases + +### Use `Parser` to prepend an ID to all selectors + +```php +$myId = "#my_id"; +$parser = new \Sabberworm\CSS\Parser($css); +$cssDocument = $parser->parse(); +foreach ($cssDocument->getAllDeclarationBlocks() as $block) { + foreach ($block->getSelectors() as $selector) { + // Loop over all selector parts (the comma-separated strings in a + // selector) and prepend the ID. + $selector->setSelector($myId.' '.$selector->getSelector()); + } +} +``` + +### Shrink all absolute sizes to half + +```php +$parser = new \Sabberworm\CSS\Parser($css); +$cssDocument = $parser->parse(); +foreach ($cssDocument->getAllValues() as $value) { + if ($value instanceof CSSSize && !$value->isRelative()) { + $value->setSize($value->getSize() / 2); + } +} +``` + +### Remove unwanted rules + +```php +$parser = new \Sabberworm\CSS\Parser($css); +$cssDocument = $parser->parse(); +foreach($cssDocument->getAllRuleSets() as $oRuleSet) { + // Note that the added dash will make this remove all rules starting with + // `font-` (like `font-size`, `font-weight`, etc.) as well as a potential + // `font-rule`. + $oRuleSet->removeRule('font-'); + $oRuleSet->removeRule('cursor'); +} +``` + +### Output + +To output the entire CSS document into a variable, just use `->render()`: + +```php +$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); +$cssDocument = $parser->parse(); +print $cssDocument->render(); +``` + +If you want to format the output, pass an instance of type `\Sabberworm\CSS\OutputFormat`: + +```php +$format = \Sabberworm\CSS\OutputFormat::create() + ->indentWithSpaces(4)->setSpaceBetweenRules("\n"); +print $cssDocument->render($format); +``` + +Or use one of the predefined formats: + +```php +print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty()); +print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact()); +``` + +To see what you can do with output formatting, look at the tests in `tests/OutputFormatTest.php`. + +## Examples + +### Example 1 (At-Rules) + +#### Input + +```css +@charset "utf-8"; + +@font-face { + font-family: "CrassRoots"; + src: url("../media/cr.ttf"); +} + +html, body { + font-size: 1.6em; +} + +@keyframes mymove { + from { top: 0px; } + to { top: 200px; } +} + +``` + +#### Structure (`var_dump()`) + +```php +class Sabberworm\CSS\CSSList\Document#4 (2) { + protected $aContents => + array(4) { + [0] => + class Sabberworm\CSS\Property\Charset#6 (2) { + private $sCharset => + class Sabberworm\CSS\Value\CSSString#5 (2) { + private $sString => + string(5) "utf-8" + protected $iLineNo => + int(1) + } + protected $iLineNo => + int(1) + } + [1] => + class Sabberworm\CSS\RuleSet\AtRuleSet#7 (4) { + private $sType => + string(9) "font-face" + private $sArgs => + string(0) "" + private $aRules => + array(2) { + 'font-family' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#8 (4) { + private $sRule => + string(11) "font-family" + private $mValue => + class Sabberworm\CSS\Value\CSSString#9 (2) { + private $sString => + string(10) "CrassRoots" + protected $iLineNo => + int(4) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(4) + } + } + 'src' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#10 (4) { + private $sRule => + string(3) "src" + private $mValue => + class Sabberworm\CSS\Value\URL#11 (2) { + private $oURL => + class Sabberworm\CSS\Value\CSSString#12 (2) { + private $sString => + string(15) "../media/cr.ttf" + protected $iLineNo => + int(5) + } + protected $iLineNo => + int(5) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(5) + } + } + } + protected $iLineNo => + int(3) + } + [2] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#13 (3) { + private $aSelectors => + array(2) { + [0] => + class Sabberworm\CSS\Property\Selector#14 (2) { + private $sSelector => + string(4) "html" + private $iSpecificity => + NULL + } + [1] => + class Sabberworm\CSS\Property\Selector#15 (2) { + private $sSelector => + string(4) "body" + private $iSpecificity => + NULL + } + } + private $aRules => + array(1) { + 'font-size' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#16 (4) { + private $sRule => + string(9) "font-size" + private $mValue => + class Sabberworm\CSS\Value\Size#17 (4) { + private $fSize => + double(1.6) + private $sUnit => + string(2) "em" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(9) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(9) + } + } + } + protected $iLineNo => + int(8) + } + [3] => + class Sabberworm\CSS\CSSList\KeyFrame#18 (4) { + private $vendorKeyFrame => + string(9) "keyframes" + private $animationName => + string(6) "mymove" + protected $aContents => + array(2) { + [0] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#19 (3) { + private $aSelectors => + array(1) { + [0] => + class Sabberworm\CSS\Property\Selector#20 (2) { + private $sSelector => + string(4) "from" + private $iSpecificity => + NULL + } + } + private $aRules => + array(1) { + 'top' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#21 (4) { + private $sRule => + string(3) "top" + private $mValue => + class Sabberworm\CSS\Value\Size#22 (4) { + private $fSize => + double(0) + private $sUnit => + string(2) "px" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(13) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(13) + } + } + } + protected $iLineNo => + int(13) + } + [1] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#23 (3) { + private $aSelectors => + array(1) { + [0] => + class Sabberworm\CSS\Property\Selector#24 (2) { + private $sSelector => + string(2) "to" + private $iSpecificity => + NULL + } + } + private $aRules => + array(1) { + 'top' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#25 (4) { + private $sRule => + string(3) "top" + private $mValue => + class Sabberworm\CSS\Value\Size#26 (4) { + private $fSize => + double(200) + private $sUnit => + string(2) "px" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(14) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(14) + } + } + } + protected $iLineNo => + int(14) + } + } + protected $iLineNo => + int(12) + } + } + protected $iLineNo => + int(1) +} + +``` + +#### Output (`render()`) + +```css +@charset "utf-8"; +@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");} +html, body {font-size: 1.6em;} +@keyframes mymove {from {top: 0px;} to {top: 200px;}} +``` + +### Example 2 (Values) + +#### Input + +```css +#header { + margin: 10px 2em 1cm 2%; + font-family: Verdana, Helvetica, "Gill Sans", sans-serif; + color: red !important; +} + +``` + +#### Structure (`var_dump()`) + +```php +class Sabberworm\CSS\CSSList\Document#4 (2) { + protected $aContents => + array(1) { + [0] => + class Sabberworm\CSS\RuleSet\DeclarationBlock#5 (3) { + private $aSelectors => + array(1) { + [0] => + class Sabberworm\CSS\Property\Selector#6 (2) { + private $sSelector => + string(7) "#header" + private $iSpecificity => + NULL + } + } + private $aRules => + array(3) { + 'margin' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#7 (4) { + private $sRule => + string(6) "margin" + private $mValue => + class Sabberworm\CSS\Value\RuleValueList#12 (3) { + protected $aComponents => + array(4) { + [0] => + class Sabberworm\CSS\Value\Size#8 (4) { + private $fSize => + double(10) + private $sUnit => + string(2) "px" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + [1] => + class Sabberworm\CSS\Value\Size#9 (4) { + private $fSize => + double(2) + private $sUnit => + string(2) "em" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + [2] => + class Sabberworm\CSS\Value\Size#10 (4) { + private $fSize => + double(1) + private $sUnit => + string(2) "cm" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + [3] => + class Sabberworm\CSS\Value\Size#11 (4) { + private $fSize => + double(2) + private $sUnit => + string(1) "%" + private $bIsColorComponent => + bool(false) + protected $iLineNo => + int(2) + } + } + protected $sSeparator => + string(1) " " + protected $iLineNo => + int(2) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(2) + } + } + 'font-family' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#13 (4) { + private $sRule => + string(11) "font-family" + private $mValue => + class Sabberworm\CSS\Value\RuleValueList#15 (3) { + protected $aComponents => + array(4) { + [0] => + string(7) "Verdana" + [1] => + string(9) "Helvetica" + [2] => + class Sabberworm\CSS\Value\CSSString#14 (2) { + private $sString => + string(9) "Gill Sans" + protected $iLineNo => + int(3) + } + [3] => + string(10) "sans-serif" + } + protected $sSeparator => + string(1) "," + protected $iLineNo => + int(3) + } + private $bIsImportant => + bool(false) + protected $iLineNo => + int(3) + } + } + 'color' => + array(1) { + [0] => + class Sabberworm\CSS\Rule\Rule#16 (4) { + private $sRule => + string(5) "color" + private $mValue => + string(3) "red" + private $bIsImportant => + bool(true) + protected $iLineNo => + int(4) + } + } + } + protected $iLineNo => + int(1) + } + } + protected $iLineNo => + int(1) +} + +``` + +#### Output (`render()`) + +```css +#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;} +``` + +## Contributors/Thanks to + +* [oliverklee](https://github.com/oliverklee) for lots of refactorings, code modernizations and CI integrations +* [raxbg](https://github.com/raxbg) for contributions to parse `calc`, grid lines, and various bugfixes. +* [westonruter](https://github.com/westonruter) for bugfixes and improvements. +* [FMCorz](https://github.com/FMCorz) for many patches and suggestions, for being able to parse comments and IE hacks (in lenient mode). +* [Lullabot](https://github.com/Lullabot) for a patch that allows to know the line number for each parsed token. +* [ju1ius](https://github.com/ju1ius) for the specificity parsing code and the ability to expand/compact shorthand properties. +* [ossinkine](https://github.com/ossinkine) for a 150 time performance boost. +* [GaryJones](https://github.com/GaryJones) for lots of input and [https://css-specificity.info/](https://css-specificity.info/). +* [docteurklein](https://github.com/docteurklein) for output formatting and `CSSList->remove()` inspiration. +* [nicolopignatelli](https://github.com/nicolopignatelli) for PSR-0 compatibility. +* [diegoembarcadero](https://github.com/diegoembarcadero) for keyframe at-rule parsing. +* [goetas](https://github.com/goetas) for @namespace at-rule support. +* [View full list](https://github.com/sabberworm/PHP-CSS-Parser/contributors) + +## Misc + +* Legacy Support: The latest pre-PSR-0 version of this project can be checked with the `0.9.0` tag. +* Running Tests: To run all unit tests for this project, run `composer install` to install phpunit and use `./vendor/bin/phpunit`. diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/composer.json b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/composer.json new file mode 100644 index 000000000..e192dd56a --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/composer.json @@ -0,0 +1,69 @@ +{ + "name": "sabberworm/php-css-parser", + "type": "library", + "description": "Parser for CSS Files written in PHP", + "keywords": [ + "parser", + "css", + "stylesheet" + ], + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "license": "MIT", + "authors": [ + { + "name": "Raphael Schweikert" + } + ], + "require": { + "php": ">=5.6.20", + "ext-iconv": "*" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36", + "codacy/coverage": "^1.4" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Sabberworm\\CSS\\Tests\\": "tests/" + } + }, + "scripts": { + "ci": [ + "@ci:static" + ], + "ci:php:fixer": "@php ./.phive/php-cs-fixer.phar --config=config/php-cs-fixer.php fix --dry-run -v --show-progress=dots bin src tests", + "ci:php:sniffer": "@php ./.phive/phpcs.phar --standard=config/phpcs.xml bin src tests", + "ci:php:stan": "@php ./.phive/phpstan.phar --configuration=config/phpstan.neon", + "ci:static": [ + "@ci:php:fixer", + "@ci:php:sniffer", + "@ci:php:stan" + ], + "fix:php": [ + "@fix:php:fixer", + "@fix:php:sniffer" + ], + "fix:php:fixer": "@php ./.phive/php-cs-fixer.phar --config=config/php-cs-fixer.php fix bin src tests", + "fix:php:sniffer": "@php ./.phive/phpcbf.phar --standard=config/phpcs.xml bin src tests", + "phpstan:baseline": "@php ./.phive/phpstan.phar --configuration=config/phpstan.neon --generate-baseline=config/phpstan-baseline.neon" + }, + "scripts-descriptions": { + "ci": "Runs all dynamic and static code checks (i.e. currently, only the static checks).", + "ci:php:fixer": "Checks the code style with PHP CS Fixer.", + "ci:php:sniffer": "Checks the code style with PHP_CodeSniffer.", + "ci:php:stan": "Checks the types with PHPStan.", + "ci:static": "Runs all static code analysis checks for the code.", + "fix:php": "Autofixes all autofixable issues in the PHP code.", + "fix:php:fixer": "Fixes autofixable issues found by PHP CS Fixer.", + "fix:php:sniffer": "Fixes autofixable issues found by PHP_CodeSniffer.", + "phpstand:baseline": "Updates the PHPStan baseline file to match the code." + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php new file mode 100644 index 000000000..218adb9a1 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php @@ -0,0 +1,83 @@ +sType = $sType; + $this->sArgs = $sArgs; + } + + /** + * @return string + */ + public function atRuleName() + { + return $this->sType; + } + + /** + * @return string + */ + public function atRuleArgs() + { + return $this->sArgs; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sArgs = $this->sArgs; + if ($sArgs) { + $sArgs = ' ' . $sArgs; + } + $sResult = $oOutputFormat->sBeforeAtRuleBlock; + $sResult .= "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{"; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + $sResult .= $oOutputFormat->sAfterAtRuleBlock; + return $sResult; + } + + /** + * @return bool + */ + public function isRootList() + { + return false; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php new file mode 100644 index 000000000..fce7913eb --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php @@ -0,0 +1,143 @@ + $aResult + * + * @return void + */ + protected function allDeclarationBlocks(array &$aResult) + { + foreach ($this->aContents as $mContent) { + if ($mContent instanceof DeclarationBlock) { + $aResult[] = $mContent; + } elseif ($mContent instanceof CSSBlockList) { + $mContent->allDeclarationBlocks($aResult); + } + } + } + + /** + * @param array $aResult + * + * @return void + */ + protected function allRuleSets(array &$aResult) + { + foreach ($this->aContents as $mContent) { + if ($mContent instanceof RuleSet) { + $aResult[] = $mContent; + } elseif ($mContent instanceof CSSBlockList) { + $mContent->allRuleSets($aResult); + } + } + } + + /** + * @param CSSList|Rule|RuleSet|Value $oElement + * @param array $aResult + * @param string|null $sSearchString + * @param bool $bSearchInFunctionArguments + * + * @return void + */ + protected function allValues($oElement, array &$aResult, $sSearchString = null, $bSearchInFunctionArguments = false) + { + if ($oElement instanceof CSSBlockList) { + foreach ($oElement->getContents() as $oContent) { + $this->allValues($oContent, $aResult, $sSearchString, $bSearchInFunctionArguments); + } + } elseif ($oElement instanceof RuleSet) { + foreach ($oElement->getRules($sSearchString) as $oRule) { + $this->allValues($oRule, $aResult, $sSearchString, $bSearchInFunctionArguments); + } + } elseif ($oElement instanceof Rule) { + $this->allValues($oElement->getValue(), $aResult, $sSearchString, $bSearchInFunctionArguments); + } elseif ($oElement instanceof ValueList) { + if ($bSearchInFunctionArguments || !($oElement instanceof CSSFunction)) { + foreach ($oElement->getListComponents() as $mComponent) { + $this->allValues($mComponent, $aResult, $sSearchString, $bSearchInFunctionArguments); + } + } + } else { + // Non-List `Value` or `CSSString` (CSS identifier) + $aResult[] = $oElement; + } + } + + /** + * @param array $aResult + * @param string|null $sSpecificitySearch + * + * @return void + */ + protected function allSelectors(array &$aResult, $sSpecificitySearch = null) + { + /** @var array $aDeclarationBlocks */ + $aDeclarationBlocks = []; + $this->allDeclarationBlocks($aDeclarationBlocks); + foreach ($aDeclarationBlocks as $oBlock) { + foreach ($oBlock->getSelectors() as $oSelector) { + if ($sSpecificitySearch === null) { + $aResult[] = $oSelector; + } else { + $sComparator = '==='; + $aSpecificitySearch = explode(' ', $sSpecificitySearch); + $iTargetSpecificity = $aSpecificitySearch[0]; + if (count($aSpecificitySearch) > 1) { + $sComparator = $aSpecificitySearch[0]; + $iTargetSpecificity = $aSpecificitySearch[1]; + } + $iTargetSpecificity = (int)$iTargetSpecificity; + $iSelectorSpecificity = $oSelector->getSpecificity(); + $bMatches = false; + switch ($sComparator) { + case '<=': + $bMatches = $iSelectorSpecificity <= $iTargetSpecificity; + break; + case '<': + $bMatches = $iSelectorSpecificity < $iTargetSpecificity; + break; + case '>=': + $bMatches = $iSelectorSpecificity >= $iTargetSpecificity; + break; + case '>': + $bMatches = $iSelectorSpecificity > $iTargetSpecificity; + break; + default: + $bMatches = $iSelectorSpecificity === $iTargetSpecificity; + break; + } + if ($bMatches) { + $aResult[] = $oSelector; + } + } + } + } + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php new file mode 100644 index 000000000..946740a49 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php @@ -0,0 +1,479 @@ + + */ + protected $aComments; + + /** + * @var array + */ + protected $aContents; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + $this->aComments = []; + $this->aContents = []; + $this->iLineNo = $iLineNo; + } + + /** + * @return void + * + * @throws UnexpectedTokenException + * @throws SourceException + */ + public static function parseList(ParserState $oParserState, CSSList $oList) + { + $bIsRoot = $oList instanceof Document; + if (is_string($oParserState)) { + $oParserState = new ParserState($oParserState, Settings::create()); + } + $bLenientParsing = $oParserState->getSettings()->bLenientParsing; + while (!$oParserState->isEnd()) { + $comments = $oParserState->consumeWhiteSpace(); + $oListItem = null; + if ($bLenientParsing) { + try { + $oListItem = self::parseListItem($oParserState, $oList); + } catch (UnexpectedTokenException $e) { + $oListItem = false; + } + } else { + $oListItem = self::parseListItem($oParserState, $oList); + } + if ($oListItem === null) { + // List parsing finished + return; + } + if ($oListItem) { + $oListItem->setComments($comments); + $oList->append($oListItem); + } + $oParserState->consumeWhiteSpace(); + } + if (!$bIsRoot && !$bLenientParsing) { + throw new SourceException("Unexpected end of document", $oParserState->currentLine()); + } + } + + /** + * @return AtRuleBlockList|KeyFrame|Charset|CSSNamespace|Import|AtRuleSet|DeclarationBlock|null|false + * + * @throws SourceException + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + private static function parseListItem(ParserState $oParserState, CSSList $oList) + { + $bIsRoot = $oList instanceof Document; + if ($oParserState->comes('@')) { + $oAtRule = self::parseAtRule($oParserState); + if ($oAtRule instanceof Charset) { + if (!$bIsRoot) { + throw new UnexpectedTokenException( + '@charset may only occur in root document', + '', + 'custom', + $oParserState->currentLine() + ); + } + if (count($oList->getContents()) > 0) { + throw new UnexpectedTokenException( + '@charset must be the first parseable token in a document', + '', + 'custom', + $oParserState->currentLine() + ); + } + $oParserState->setCharset($oAtRule->getCharset()->getString()); + } + return $oAtRule; + } elseif ($oParserState->comes('}')) { + if (!$oParserState->getSettings()->bLenientParsing) { + throw new UnexpectedTokenException('CSS selector', '}', 'identifier', $oParserState->currentLine()); + } else { + if ($bIsRoot) { + if ($oParserState->getSettings()->bLenientParsing) { + return DeclarationBlock::parse($oParserState); + } else { + throw new SourceException("Unopened {", $oParserState->currentLine()); + } + } else { + return null; + } + } + } else { + return DeclarationBlock::parse($oParserState, $oList); + } + } + + /** + * @param ParserState $oParserState + * + * @return AtRuleBlockList|KeyFrame|Charset|CSSNamespace|Import|AtRuleSet|null + * + * @throws SourceException + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + private static function parseAtRule(ParserState $oParserState) + { + $oParserState->consume('@'); + $sIdentifier = $oParserState->parseIdentifier(); + $iIdentifierLineNum = $oParserState->currentLine(); + $oParserState->consumeWhiteSpace(); + if ($sIdentifier === 'import') { + $oLocation = URL::parse($oParserState); + $oParserState->consumeWhiteSpace(); + $sMediaQuery = null; + if (!$oParserState->comes(';')) { + $sMediaQuery = trim($oParserState->consumeUntil([';', ParserState::EOF])); + } + $oParserState->consumeUntil([';', ParserState::EOF], true, true); + return new Import($oLocation, $sMediaQuery ?: null, $iIdentifierLineNum); + } elseif ($sIdentifier === 'charset') { + $sCharset = CSSString::parse($oParserState); + $oParserState->consumeWhiteSpace(); + $oParserState->consumeUntil([';', ParserState::EOF], true, true); + return new Charset($sCharset, $iIdentifierLineNum); + } elseif (self::identifierIs($sIdentifier, 'keyframes')) { + $oResult = new KeyFrame($iIdentifierLineNum); + $oResult->setVendorKeyFrame($sIdentifier); + $oResult->setAnimationName(trim($oParserState->consumeUntil('{', false, true))); + CSSList::parseList($oParserState, $oResult); + if ($oParserState->comes('}')) { + $oParserState->consume('}'); + } + return $oResult; + } elseif ($sIdentifier === 'namespace') { + $sPrefix = null; + $mUrl = Value::parsePrimitiveValue($oParserState); + if (!$oParserState->comes(';')) { + $sPrefix = $mUrl; + $mUrl = Value::parsePrimitiveValue($oParserState); + } + $oParserState->consumeUntil([';', ParserState::EOF], true, true); + if ($sPrefix !== null && !is_string($sPrefix)) { + throw new UnexpectedTokenException('Wrong namespace prefix', $sPrefix, 'custom', $iIdentifierLineNum); + } + if (!($mUrl instanceof CSSString || $mUrl instanceof URL)) { + throw new UnexpectedTokenException( + 'Wrong namespace url of invalid type', + $mUrl, + 'custom', + $iIdentifierLineNum + ); + } + return new CSSNamespace($mUrl, $sPrefix, $iIdentifierLineNum); + } else { + // Unknown other at rule (font-face or such) + $sArgs = trim($oParserState->consumeUntil('{', false, true)); + if (substr_count($sArgs, "(") != substr_count($sArgs, ")")) { + if ($oParserState->getSettings()->bLenientParsing) { + return null; + } else { + throw new SourceException("Unmatched brace count in media query", $oParserState->currentLine()); + } + } + $bUseRuleSet = true; + foreach (explode('/', AtRule::BLOCK_RULES) as $sBlockRuleName) { + if (self::identifierIs($sIdentifier, $sBlockRuleName)) { + $bUseRuleSet = false; + break; + } + } + if ($bUseRuleSet) { + $oAtRule = new AtRuleSet($sIdentifier, $sArgs, $iIdentifierLineNum); + RuleSet::parseRuleSet($oParserState, $oAtRule); + } else { + $oAtRule = new AtRuleBlockList($sIdentifier, $sArgs, $iIdentifierLineNum); + CSSList::parseList($oParserState, $oAtRule); + if ($oParserState->comes('}')) { + $oParserState->consume('}'); + } + } + return $oAtRule; + } + } + + /** + * Tests an identifier for a given value. Since identifiers are all keywords, they can be vendor-prefixed. + * We need to check for these versions too. + * + * @param string $sIdentifier + * @param string $sMatch + * + * @return bool + */ + private static function identifierIs($sIdentifier, $sMatch) + { + return (strcasecmp($sIdentifier, $sMatch) === 0) + ?: preg_match("/^(-\\w+-)?$sMatch$/i", $sIdentifier) === 1; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * Prepends an item to the list of contents. + * + * @param RuleSet|CSSList|Import|Charset $oItem + * + * @return void + */ + public function prepend($oItem) + { + array_unshift($this->aContents, $oItem); + } + + /** + * Appends an item to tje list of contents. + * + * @param RuleSet|CSSList|Import|Charset $oItem + * + * @return void + */ + public function append($oItem) + { + $this->aContents[] = $oItem; + } + + /** + * Splices the list of contents. + * + * @param int $iOffset + * @param int $iLength + * @param array $mReplacement + * + * @return void + */ + public function splice($iOffset, $iLength = null, $mReplacement = null) + { + array_splice($this->aContents, $iOffset, $iLength, $mReplacement); + } + + /** + * Removes an item from the CSS list. + * + * @param RuleSet|Import|Charset|CSSList $oItemToRemove + * May be a RuleSet (most likely a DeclarationBlock), a Import, + * a Charset or another CSSList (most likely a MediaQuery) + * + * @return bool whether the item was removed + */ + public function remove($oItemToRemove) + { + $iKey = array_search($oItemToRemove, $this->aContents, true); + if ($iKey !== false) { + unset($this->aContents[$iKey]); + return true; + } + return false; + } + + /** + * Replaces an item from the CSS list. + * + * @param RuleSet|Import|Charset|CSSList $oOldItem + * May be a `RuleSet` (most likely a `DeclarationBlock`), an `Import`, a `Charset` + * or another `CSSList` (most likely a `MediaQuery`) + * + * @return bool + */ + public function replace($oOldItem, $mNewItem) + { + $iKey = array_search($oOldItem, $this->aContents, true); + if ($iKey !== false) { + if (is_array($mNewItem)) { + array_splice($this->aContents, $iKey, 1, $mNewItem); + } else { + array_splice($this->aContents, $iKey, 1, [$mNewItem]); + } + return true; + } + return false; + } + + /** + * @param array $aContents + */ + public function setContents(array $aContents) + { + $this->aContents = []; + foreach ($aContents as $content) { + $this->append($content); + } + } + + /** + * Removes a declaration block from the CSS list if it matches all given selectors. + * + * @param DeclarationBlock|array|string $mSelector the selectors to match + * @param bool $bRemoveAll whether to stop at the first declaration block found or remove all blocks + * + * @return void + */ + public function removeDeclarationBlockBySelector($mSelector, $bRemoveAll = false) + { + if ($mSelector instanceof DeclarationBlock) { + $mSelector = $mSelector->getSelectors(); + } + if (!is_array($mSelector)) { + $mSelector = explode(',', $mSelector); + } + foreach ($mSelector as $iKey => &$mSel) { + if (!($mSel instanceof Selector)) { + if (!Selector::isValid($mSel)) { + throw new UnexpectedTokenException( + "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.", + $mSel, + "custom" + ); + } + $mSel = new Selector($mSel); + } + } + foreach ($this->aContents as $iKey => $mItem) { + if (!($mItem instanceof DeclarationBlock)) { + continue; + } + if ($mItem->getSelectors() == $mSelector) { + unset($this->aContents[$iKey]); + if (!$bRemoveAll) { + return; + } + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = ''; + $bIsFirst = true; + $oNextLevel = $oOutputFormat; + if (!$this->isRootList()) { + $oNextLevel = $oOutputFormat->nextLevel(); + } + foreach ($this->aContents as $oContent) { + $sRendered = $oOutputFormat->safely(function () use ($oNextLevel, $oContent) { + return $oContent->render($oNextLevel); + }); + if ($sRendered === null) { + continue; + } + if ($bIsFirst) { + $bIsFirst = false; + $sResult .= $oNextLevel->spaceBeforeBlocks(); + } else { + $sResult .= $oNextLevel->spaceBetweenBlocks(); + } + $sResult .= $sRendered; + } + + if (!$bIsFirst) { + // Had some output + $sResult .= $oOutputFormat->spaceAfterBlocks(); + } + + return $sResult; + } + + /** + * Return true if the list can not be further outdented. Only important when rendering. + * + * @return bool + */ + abstract public function isRootList(); + + /** + * @return array + */ + public function getContents() + { + return $this->aContents; + } + + /** + * @param array $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/Document.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/Document.php new file mode 100644 index 000000000..91ab2c6b2 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/Document.php @@ -0,0 +1,172 @@ +currentLine()); + CSSList::parseList($oParserState, $oDocument); + return $oDocument; + } + + /** + * Gets all `DeclarationBlock` objects recursively. + * + * @return array + */ + public function getAllDeclarationBlocks() + { + /** @var array $aResult */ + $aResult = []; + $this->allDeclarationBlocks($aResult); + return $aResult; + } + + /** + * Gets all `DeclarationBlock` objects recursively. + * + * @return array + * + * @deprecated will be removed in version 9.0; use `getAllDeclarationBlocks()` instead + */ + public function getAllSelectors() + { + return $this->getAllDeclarationBlocks(); + } + + /** + * Returns all `RuleSet` objects found recursively in the tree. + * + * @return array + */ + public function getAllRuleSets() + { + /** @var array $aResult */ + $aResult = []; + $this->allRuleSets($aResult); + return $aResult; + } + + /** + * Returns all `Value` objects found recursively in the tree. + * + * @param CSSList|RuleSet|string $mElement + * the `CSSList` or `RuleSet` to start the search from (defaults to the whole document). + * If a string is given, it is used as rule name filter. + * @param bool $bSearchInFunctionArguments whether to also return Value objects used as Function arguments. + * + * @return array + * + * @see RuleSet->getRules() + */ + public function getAllValues($mElement = null, $bSearchInFunctionArguments = false) + { + $sSearchString = null; + if ($mElement === null) { + $mElement = $this; + } elseif (is_string($mElement)) { + $sSearchString = $mElement; + $mElement = $this; + } + /** @var array $aResult */ + $aResult = []; + $this->allValues($mElement, $aResult, $sSearchString, $bSearchInFunctionArguments); + return $aResult; + } + + /** + * Returns all `Selector` objects found recursively in the tree. + * + * Note that this does not yield the full `DeclarationBlock` that the selector belongs to + * (and, currently, there is no way to get to that). + * + * @param string|null $sSpecificitySearch + * An optional filter by specificity. + * May contain a comparison operator and a number or just a number (defaults to "=="). + * + * @return array + * @example `getSelectorsBySpecificity('>= 100')` + * + */ + public function getSelectorsBySpecificity($sSpecificitySearch = null) + { + /** @var array $aResult */ + $aResult = []; + $this->allSelectors($aResult, $sSpecificitySearch); + return $aResult; + } + + /** + * Expands all shorthand properties to their long value. + * + * @return void + */ + public function expandShorthands() + { + foreach ($this->getAllDeclarationBlocks() as $oDeclaration) { + $oDeclaration->expandShorthands(); + } + } + + /** + * Create shorthands properties whenever possible. + * + * @return void + */ + public function createShorthands() + { + foreach ($this->getAllDeclarationBlocks() as $oDeclaration) { + $oDeclaration->createShorthands(); + } + } + + /** + * Overrides `render()` to make format argument optional. + * + * @param OutputFormat|null $oOutputFormat + * + * @return string + */ + public function render(OutputFormat $oOutputFormat = null) + { + if ($oOutputFormat === null) { + $oOutputFormat = new OutputFormat(); + } + return parent::render($oOutputFormat); + } + + /** + * @return bool + */ + public function isRootList() + { + return true; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php new file mode 100644 index 000000000..d9420e9c0 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php @@ -0,0 +1,104 @@ +vendorKeyFrame = null; + $this->animationName = null; + } + + /** + * @param string $vendorKeyFrame + */ + public function setVendorKeyFrame($vendorKeyFrame) + { + $this->vendorKeyFrame = $vendorKeyFrame; + } + + /** + * @return string|null + */ + public function getVendorKeyFrame() + { + return $this->vendorKeyFrame; + } + + /** + * @param string $animationName + */ + public function setAnimationName($animationName) + { + $this->animationName = $animationName; + } + + /** + * @return string|null + */ + public function getAnimationName() + { + return $this->animationName; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = "@{$this->vendorKeyFrame} {$this->animationName}{$oOutputFormat->spaceBeforeOpeningBrace()}{"; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + return $sResult; + } + + /** + * @return bool + */ + public function isRootList() + { + return false; + } + + /** + * @return string|null + */ + public function atRuleName() + { + return $this->vendorKeyFrame; + } + + /** + * @return string|null + */ + public function atRuleArgs() + { + return $this->animationName; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Comment.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Comment.php new file mode 100644 index 000000000..6128d7498 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Comment.php @@ -0,0 +1,71 @@ +sComment = $sComment; + $this->iLineNo = $iLineNo; + } + + /** + * @return string + */ + public function getComment() + { + return $this->sComment; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param string $sComment + * + * @return void + */ + public function setComment($sComment) + { + $this->sComment = $sComment; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return '/*' . $this->sComment . '*/'; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php new file mode 100644 index 000000000..5e450bfb3 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Comment/Commentable.php @@ -0,0 +1,25 @@ + $aComments + * + * @return void + */ + public function addComments(array $aComments); + + /** + * @return array + */ + public function getComments(); + + /** + * @param array $aComments + * + * @return void + */ + public function setComments(array $aComments); +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormat.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormat.php new file mode 100644 index 000000000..595d30643 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormat.php @@ -0,0 +1,334 @@ +set('Space*Rules', "\n");`) + */ + public $sSpaceAfterRuleName = ' '; + + /** + * @var string + */ + public $sSpaceBeforeRules = ''; + + /** + * @var string + */ + public $sSpaceAfterRules = ''; + + /** + * @var string + */ + public $sSpaceBetweenRules = ''; + + /** + * @var string + */ + public $sSpaceBeforeBlocks = ''; + + /** + * @var string + */ + public $sSpaceAfterBlocks = ''; + + /** + * @var string + */ + public $sSpaceBetweenBlocks = "\n"; + + /** + * Content injected in and around at-rule blocks. + * + * @var string + */ + public $sBeforeAtRuleBlock = ''; + + /** + * @var string + */ + public $sAfterAtRuleBlock = ''; + + /** + * This is what’s printed before and after the comma if a declaration block contains multiple selectors. + * + * @var string + */ + public $sSpaceBeforeSelectorSeparator = ''; + + /** + * @var string + */ + public $sSpaceAfterSelectorSeparator = ' '; + + /** + * This is what’s printed after the comma of value lists + * + * @var string + */ + public $sSpaceBeforeListArgumentSeparator = ''; + + /** + * @var string + */ + public $sSpaceAfterListArgumentSeparator = ''; + + /** + * @var string + */ + public $sSpaceBeforeOpeningBrace = ' '; + + /** + * Content injected in and around declaration blocks. + * + * @var string + */ + public $sBeforeDeclarationBlock = ''; + + /** + * @var string + */ + public $sAfterDeclarationBlockSelectors = ''; + + /** + * @var string + */ + public $sAfterDeclarationBlock = ''; + + /** + * Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings. + * + * @var string + */ + public $sIndentation = "\t"; + + /** + * Output exceptions. + * + * @var bool + */ + public $bIgnoreExceptions = false; + + /** + * @var OutputFormatter|null + */ + private $oFormatter = null; + + /** + * @var OutputFormat|null + */ + private $oNextLevelFormat = null; + + /** + * @var int + */ + private $iIndentationLevel = 0; + + public function __construct() + { + } + + /** + * @param string $sName + * + * @return string|null + */ + public function get($sName) + { + $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i']; + foreach ($aVarPrefixes as $sPrefix) { + $sFieldName = $sPrefix . ucfirst($sName); + if (isset($this->$sFieldName)) { + return $this->$sFieldName; + } + } + return null; + } + + /** + * @param array|string $aNames + * @param mixed $mValue + * + * @return self|false + */ + public function set($aNames, $mValue) + { + $aVarPrefixes = ['a', 's', 'm', 'b', 'f', 'o', 'c', 'i']; + if (is_string($aNames) && strpos($aNames, '*') !== false) { + $aNames = + [ + str_replace('*', 'Before', $aNames), + str_replace('*', 'Between', $aNames), + str_replace('*', 'After', $aNames), + ]; + } elseif (!is_array($aNames)) { + $aNames = [$aNames]; + } + foreach ($aVarPrefixes as $sPrefix) { + $bDidReplace = false; + foreach ($aNames as $sName) { + $sFieldName = $sPrefix . ucfirst($sName); + if (isset($this->$sFieldName)) { + $this->$sFieldName = $mValue; + $bDidReplace = true; + } + } + if ($bDidReplace) { + return $this; + } + } + // Break the chain so the user knows this option is invalid + return false; + } + + /** + * @param string $sMethodName + * @param array $aArguments + * + * @return mixed + * + * @throws \Exception + */ + public function __call($sMethodName, array $aArguments) + { + if (strpos($sMethodName, 'set') === 0) { + return $this->set(substr($sMethodName, 3), $aArguments[0]); + } elseif (strpos($sMethodName, 'get') === 0) { + return $this->get(substr($sMethodName, 3)); + } elseif (method_exists(OutputFormatter::class, $sMethodName)) { + return call_user_func_array([$this->getFormatter(), $sMethodName], $aArguments); + } else { + throw new \Exception('Unknown OutputFormat method called: ' . $sMethodName); + } + } + + /** + * @param int $iNumber + * + * @return self + */ + public function indentWithTabs($iNumber = 1) + { + return $this->setIndentation(str_repeat("\t", $iNumber)); + } + + /** + * @param int $iNumber + * + * @return self + */ + public function indentWithSpaces($iNumber = 2) + { + return $this->setIndentation(str_repeat(" ", $iNumber)); + } + + /** + * @return OutputFormat + */ + public function nextLevel() + { + if ($this->oNextLevelFormat === null) { + $this->oNextLevelFormat = clone $this; + $this->oNextLevelFormat->iIndentationLevel++; + $this->oNextLevelFormat->oFormatter = null; + } + return $this->oNextLevelFormat; + } + + /** + * @return void + */ + public function beLenient() + { + $this->bIgnoreExceptions = true; + } + + /** + * @return OutputFormatter + */ + public function getFormatter() + { + if ($this->oFormatter === null) { + $this->oFormatter = new OutputFormatter($this); + } + return $this->oFormatter; + } + + /** + * @return int + */ + public function level() + { + return $this->iIndentationLevel; + } + + /** + * Creates an instance of this class without any particular formatting settings. + * + * @return self + */ + public static function create() + { + return new OutputFormat(); + } + + /** + * Creates an instance of this class with a preset for compact formatting. + * + * @return self + */ + public static function createCompact() + { + $format = self::create(); + $format->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('') + ->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator(''); + return $format; + } + + /** + * Creates an instance of this class with a preset for pretty formatting. + * + * @return self + */ + public static function createPretty() + { + $format = self::create(); + $format->set('Space*Rules', "\n")->set('Space*Blocks', "\n") + ->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', ['default' => '', ',' => ' ']); + return $format; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormatter.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormatter.php new file mode 100644 index 000000000..535feca7f --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/OutputFormatter.php @@ -0,0 +1,231 @@ +oFormat = $oFormat; + } + + /** + * @param string $sName + * @param string|null $sType + * + * @return string + */ + public function space($sName, $sType = null) + { + $sSpaceString = $this->oFormat->get("Space$sName"); + // If $sSpaceString is an array, we have multiple values configured + // depending on the type of object the space applies to + if (is_array($sSpaceString)) { + if ($sType !== null && isset($sSpaceString[$sType])) { + $sSpaceString = $sSpaceString[$sType]; + } else { + $sSpaceString = reset($sSpaceString); + } + } + return $this->prepareSpace($sSpaceString); + } + + /** + * @return string + */ + public function spaceAfterRuleName() + { + return $this->space('AfterRuleName'); + } + + /** + * @return string + */ + public function spaceBeforeRules() + { + return $this->space('BeforeRules'); + } + + /** + * @return string + */ + public function spaceAfterRules() + { + return $this->space('AfterRules'); + } + + /** + * @return string + */ + public function spaceBetweenRules() + { + return $this->space('BetweenRules'); + } + + /** + * @return string + */ + public function spaceBeforeBlocks() + { + return $this->space('BeforeBlocks'); + } + + /** + * @return string + */ + public function spaceAfterBlocks() + { + return $this->space('AfterBlocks'); + } + + /** + * @return string + */ + public function spaceBetweenBlocks() + { + return $this->space('BetweenBlocks'); + } + + /** + * @return string + */ + public function spaceBeforeSelectorSeparator() + { + return $this->space('BeforeSelectorSeparator'); + } + + /** + * @return string + */ + public function spaceAfterSelectorSeparator() + { + return $this->space('AfterSelectorSeparator'); + } + + /** + * @param string $sSeparator + * + * @return string + */ + public function spaceBeforeListArgumentSeparator($sSeparator) + { + return $this->space('BeforeListArgumentSeparator', $sSeparator); + } + + /** + * @param string $sSeparator + * + * @return string + */ + public function spaceAfterListArgumentSeparator($sSeparator) + { + return $this->space('AfterListArgumentSeparator', $sSeparator); + } + + /** + * @return string + */ + public function spaceBeforeOpeningBrace() + { + return $this->space('BeforeOpeningBrace'); + } + + /** + * Runs the given code, either swallowing or passing exceptions, depending on the `bIgnoreExceptions` setting. + * + * @param string $cCode the name of the function to call + * + * @return string|null + */ + public function safely($cCode) + { + if ($this->oFormat->get('IgnoreExceptions')) { + // If output exceptions are ignored, run the code with exception guards + try { + return $cCode(); + } catch (OutputException $e) { + return null; + } // Do nothing + } else { + // Run the code as-is + return $cCode(); + } + } + + /** + * Clone of the `implode` function, but calls `render` with the current output format instead of `__toString()`. + * + * @param string $sSeparator + * @param array $aValues + * @param bool $bIncreaseLevel + * + * @return string + */ + public function implode($sSeparator, array $aValues, $bIncreaseLevel = false) + { + $sResult = ''; + $oFormat = $this->oFormat; + if ($bIncreaseLevel) { + $oFormat = $oFormat->nextLevel(); + } + $bIsFirst = true; + foreach ($aValues as $mValue) { + if ($bIsFirst) { + $bIsFirst = false; + } else { + $sResult .= $sSeparator; + } + if ($mValue instanceof Renderable) { + $sResult .= $mValue->render($oFormat); + } else { + $sResult .= $mValue; + } + } + return $sResult; + } + + /** + * @param string $sString + * + * @return string + */ + public function removeLastSemicolon($sString) + { + if ($this->oFormat->get('SemicolonAfterLastRule')) { + return $sString; + } + $sString = explode(';', $sString); + if (count($sString) < 2) { + return $sString[0]; + } + $sLast = array_pop($sString); + $sNextToLast = array_pop($sString); + array_push($sString, $sNextToLast . $sLast); + return implode(';', $sString); + } + + /** + * @param string $sSpaceString + * + * @return string + */ + private function prepareSpace($sSpaceString) + { + return str_replace("\n", "\n" . $this->indent(), $sSpaceString); + } + + /** + * @return string + */ + private function indent() + { + return str_repeat($this->oFormat->sIndentation, $this->oFormat->level()); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parser.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parser.php new file mode 100644 index 000000000..f3b0493a5 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parser.php @@ -0,0 +1,60 @@ +oParserState = new ParserState($sText, $oParserSettings, $iLineNo); + } + + /** + * @param string $sCharset + * + * @return void + */ + public function setCharset($sCharset) + { + $this->oParserState->setCharset($sCharset); + } + + /** + * @return void + */ + public function getCharset() + { + // Note: The `return` statement is missing here. This is a bug that needs to be fixed. + $this->oParserState->getCharset(); + } + + /** + * @return Document + * + * @throws SourceException + */ + public function parse() + { + return Document::parse($this->oParserState); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php new file mode 100644 index 000000000..9bfbc75fb --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php @@ -0,0 +1,18 @@ + + */ + private $aText; + + /** + * @var int + */ + private $iCurrentPosition; + + /** + * @var string + */ + private $sCharset; + + /** + * @var int + */ + private $iLength; + + /** + * @var int + */ + private $iLineNo; + + /** + * @param string $sText + * @param int $iLineNo + */ + public function __construct($sText, Settings $oParserSettings, $iLineNo = 1) + { + $this->oParserSettings = $oParserSettings; + $this->sText = $sText; + $this->iCurrentPosition = 0; + $this->iLineNo = $iLineNo; + $this->setCharset($this->oParserSettings->sDefaultCharset); + } + + /** + * @param string $sCharset + * + * @return void + */ + public function setCharset($sCharset) + { + $this->sCharset = $sCharset; + $this->aText = $this->strsplit($this->sText); + if (is_array($this->aText)) { + $this->iLength = count($this->aText); + } + } + + /** + * @return string + */ + public function getCharset() + { + return $this->sCharset; + } + + /** + * @return int + */ + public function currentLine() + { + return $this->iLineNo; + } + + /** + * @return int + */ + public function currentColumn() + { + return $this->iCurrentPosition; + } + + /** + * @return Settings + */ + public function getSettings() + { + return $this->oParserSettings; + } + + /** + * @param bool $bIgnoreCase + * + * @return string + * + * @throws UnexpectedTokenException + */ + public function parseIdentifier($bIgnoreCase = true) + { + $sResult = $this->parseCharacter(true); + if ($sResult === null) { + throw new UnexpectedTokenException($sResult, $this->peek(5), 'identifier', $this->iLineNo); + } + $sCharacter = null; + while (($sCharacter = $this->parseCharacter(true)) !== null) { + if (preg_match('/[a-zA-Z0-9\x{00A0}-\x{FFFF}_-]/Sux', $sCharacter)) { + $sResult .= $sCharacter; + } else { + $sResult .= '\\' . $sCharacter; + } + } + if ($bIgnoreCase) { + $sResult = $this->strtolower($sResult); + } + return $sResult; + } + + /** + * @param bool $bIsForIdentifier + * + * @return string|null + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function parseCharacter($bIsForIdentifier) + { + if ($this->peek() === '\\') { + if ( + $bIsForIdentifier && $this->oParserSettings->bLenientParsing + && ($this->comes('\0') || $this->comes('\9')) + ) { + // Non-strings can contain \0 or \9 which is an IE hack supported in lenient parsing. + return null; + } + $this->consume('\\'); + if ($this->comes('\n') || $this->comes('\r')) { + return ''; + } + if (preg_match('/[0-9a-fA-F]/Su', $this->peek()) === 0) { + return $this->consume(1); + } + $sUnicode = $this->consumeExpression('/^[0-9a-fA-F]{1,6}/u', 6); + if ($this->strlen($sUnicode) < 6) { + // Consume whitespace after incomplete unicode escape + if (preg_match('/\\s/isSu', $this->peek())) { + if ($this->comes('\r\n')) { + $this->consume(2); + } else { + $this->consume(1); + } + } + } + $iUnicode = intval($sUnicode, 16); + $sUtf32 = ""; + for ($i = 0; $i < 4; ++$i) { + $sUtf32 .= chr($iUnicode & 0xff); + $iUnicode = $iUnicode >> 8; + } + return iconv('utf-32le', $this->sCharset, $sUtf32); + } + if ($bIsForIdentifier) { + $peek = ord($this->peek()); + // Ranges: a-z A-Z 0-9 - _ + if ( + ($peek >= 97 && $peek <= 122) + || ($peek >= 65 && $peek <= 90) + || ($peek >= 48 && $peek <= 57) + || ($peek === 45) + || ($peek === 95) + || ($peek > 0xa1) + ) { + return $this->consume(1); + } + } else { + return $this->consume(1); + } + return null; + } + + /** + * @return array|void + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consumeWhiteSpace() + { + $comments = []; + do { + while (preg_match('/\\s/isSu', $this->peek()) === 1) { + $this->consume(1); + } + if ($this->oParserSettings->bLenientParsing) { + try { + $oComment = $this->consumeComment(); + } catch (UnexpectedEOFException $e) { + $this->iCurrentPosition = $this->iLength; + return; + } + } else { + $oComment = $this->consumeComment(); + } + if ($oComment !== false) { + $comments[] = $oComment; + } + } while ($oComment !== false); + return $comments; + } + + /** + * @param string $sString + * @param bool $bCaseInsensitive + * + * @return bool + */ + public function comes($sString, $bCaseInsensitive = false) + { + $sPeek = $this->peek(strlen($sString)); + return ($sPeek == '') + ? false + : $this->streql($sPeek, $sString, $bCaseInsensitive); + } + + /** + * @param int $iLength + * @param int $iOffset + * + * @return string + */ + public function peek($iLength = 1, $iOffset = 0) + { + $iOffset += $this->iCurrentPosition; + if ($iOffset >= $this->iLength) { + return ''; + } + return $this->substr($iOffset, $iLength); + } + + /** + * @param int $mValue + * + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consume($mValue = 1) + { + if (is_string($mValue)) { + $iLineCount = substr_count($mValue, "\n"); + $iLength = $this->strlen($mValue); + if (!$this->streql($this->substr($this->iCurrentPosition, $iLength), $mValue)) { + throw new UnexpectedTokenException($mValue, $this->peek(max($iLength, 5)), $this->iLineNo); + } + $this->iLineNo += $iLineCount; + $this->iCurrentPosition += $this->strlen($mValue); + return $mValue; + } else { + if ($this->iCurrentPosition + $mValue > $this->iLength) { + throw new UnexpectedEOFException($mValue, $this->peek(5), 'count', $this->iLineNo); + } + $sResult = $this->substr($this->iCurrentPosition, $mValue); + $iLineCount = substr_count($sResult, "\n"); + $this->iLineNo += $iLineCount; + $this->iCurrentPosition += $mValue; + return $sResult; + } + } + + /** + * @param string $mExpression + * @param int|null $iMaxLength + * + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consumeExpression($mExpression, $iMaxLength = null) + { + $aMatches = null; + $sInput = $iMaxLength !== null ? $this->peek($iMaxLength) : $this->inputLeft(); + if (preg_match($mExpression, $sInput, $aMatches, PREG_OFFSET_CAPTURE) === 1) { + return $this->consume($aMatches[0][0]); + } + throw new UnexpectedTokenException($mExpression, $this->peek(5), 'expression', $this->iLineNo); + } + + /** + * @return Comment|false + */ + public function consumeComment() + { + $mComment = false; + if ($this->comes('/*')) { + $iLineNo = $this->iLineNo; + $this->consume(1); + $mComment = ''; + while (($char = $this->consume(1)) !== '') { + $mComment .= $char; + if ($this->comes('*/')) { + $this->consume(2); + break; + } + } + } + + if ($mComment !== false) { + // We skip the * which was included in the comment. + return new Comment(substr($mComment, 1), $iLineNo); + } + + return $mComment; + } + + /** + * @return bool + */ + public function isEnd() + { + return $this->iCurrentPosition >= $this->iLength; + } + + /** + * @param array|string $aEnd + * @param string $bIncludeEnd + * @param string $consumeEnd + * @param array $comments + * + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public function consumeUntil($aEnd, $bIncludeEnd = false, $consumeEnd = false, array &$comments = []) + { + $aEnd = is_array($aEnd) ? $aEnd : [$aEnd]; + $out = ''; + $start = $this->iCurrentPosition; + + while (!$this->isEnd()) { + $char = $this->consume(1); + if (in_array($char, $aEnd)) { + if ($bIncludeEnd) { + $out .= $char; + } elseif (!$consumeEnd) { + $this->iCurrentPosition -= $this->strlen($char); + } + return $out; + } + $out .= $char; + if ($comment = $this->consumeComment()) { + $comments[] = $comment; + } + } + + if (in_array(self::EOF, $aEnd)) { + return $out; + } + + $this->iCurrentPosition = $start; + throw new UnexpectedEOFException( + 'One of ("' . implode('","', $aEnd) . '")', + $this->peek(5), + 'search', + $this->iLineNo + ); + } + + /** + * @return string + */ + private function inputLeft() + { + return $this->substr($this->iCurrentPosition, -1); + } + + /** + * @param string $sString1 + * @param string $sString2 + * @param bool $bCaseInsensitive + * + * @return bool + */ + public function streql($sString1, $sString2, $bCaseInsensitive = true) + { + if ($bCaseInsensitive) { + return $this->strtolower($sString1) === $this->strtolower($sString2); + } else { + return $sString1 === $sString2; + } + } + + /** + * @param int $iAmount + * + * @return void + */ + public function backtrack($iAmount) + { + $this->iCurrentPosition -= $iAmount; + } + + /** + * @param string $sString + * + * @return int + */ + public function strlen($sString) + { + if ($this->oParserSettings->bMultibyteSupport) { + return mb_strlen($sString, $this->sCharset); + } else { + return strlen($sString); + } + } + + /** + * @param int $iStart + * @param int $iLength + * + * @return string + */ + private function substr($iStart, $iLength) + { + if ($iLength < 0) { + $iLength = $this->iLength - $iStart + $iLength; + } + if ($iStart + $iLength > $this->iLength) { + $iLength = $this->iLength - $iStart; + } + $sResult = ''; + while ($iLength > 0) { + $sResult .= $this->aText[$iStart]; + $iStart++; + $iLength--; + } + return $sResult; + } + + /** + * @param string $sString + * + * @return string + */ + private function strtolower($sString) + { + if ($this->oParserSettings->bMultibyteSupport) { + return mb_strtolower($sString, $this->sCharset); + } else { + return strtolower($sString); + } + } + + /** + * @param string $sString + * + * @return array + */ + private function strsplit($sString) + { + if ($this->oParserSettings->bMultibyteSupport) { + if ($this->streql($this->sCharset, 'utf-8')) { + return preg_split('//u', $sString, -1, PREG_SPLIT_NO_EMPTY); + } else { + $iLength = mb_strlen($sString, $this->sCharset); + $aResult = []; + for ($i = 0; $i < $iLength; ++$i) { + $aResult[] = mb_substr($sString, $i, 1, $this->sCharset); + } + return $aResult; + } + } else { + if ($sString === '') { + return []; + } else { + return str_split($sString); + } + } + } + + /** + * @param string $sString + * @param string $sNeedle + * @param int $iOffset + * + * @return int|false + */ + private function strpos($sString, $sNeedle, $iOffset) + { + if ($this->oParserSettings->bMultibyteSupport) { + return mb_strpos($sString, $sNeedle, $iOffset, $this->sCharset); + } else { + return strpos($sString, $sNeedle, $iOffset); + } + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php new file mode 100644 index 000000000..1ca668a99 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php @@ -0,0 +1,32 @@ +iLineNo = $iLineNo; + if (!empty($iLineNo)) { + $sMessage .= " [line no: $iLineNo]"; + } + parent::__construct($sMessage); + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php new file mode 100644 index 000000000..368ec70c7 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php @@ -0,0 +1,12 @@ +sExpected = $sExpected; + $this->sFound = $sFound; + $this->sMatchType = $sMatchType; + $sMessage = "Token “{$sExpected}” ({$sMatchType}) not found. Got “{$sFound}”."; + if ($this->sMatchType === 'search') { + $sMessage = "Search for “{$sExpected}” returned no results. Context: “{$sFound}”."; + } elseif ($this->sMatchType === 'count') { + $sMessage = "Next token was expected to have {$sExpected} chars. Context: “{$sFound}”."; + } elseif ($this->sMatchType === 'identifier') { + $sMessage = "Identifier expected. Got “{$sFound}”"; + } elseif ($this->sMatchType === 'custom') { + $sMessage = trim("$sExpected $sFound"); + } + + parent::__construct($sMessage, $iLineNo); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/AtRule.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/AtRule.php new file mode 100644 index 000000000..9536ff5e9 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/AtRule.php @@ -0,0 +1,34 @@ + + */ + protected $aComments; + + /** + * @param string $mUrl + * @param string|null $sPrefix + * @param int $iLineNo + */ + public function __construct($mUrl, $sPrefix = null, $iLineNo = 0) + { + $this->mUrl = $mUrl; + $this->sPrefix = $sPrefix; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return '@namespace ' . ($this->sPrefix === null ? '' : $this->sPrefix . ' ') + . $this->mUrl->render($oOutputFormat) . ';'; + } + + /** + * @return string + */ + public function getUrl() + { + return $this->mUrl; + } + + /** + * @return string|null + */ + public function getPrefix() + { + return $this->sPrefix; + } + + /** + * @param string $mUrl + * + * @return void + */ + public function setUrl($mUrl) + { + $this->mUrl = $mUrl; + } + + /** + * @param string $sPrefix + * + * @return void + */ + public function setPrefix($sPrefix) + { + $this->sPrefix = $sPrefix; + } + + /** + * @return string + */ + public function atRuleName() + { + return 'namespace'; + } + + /** + * @return array + */ + public function atRuleArgs() + { + $aResult = [$this->mUrl]; + if ($this->sPrefix) { + array_unshift($aResult, $this->sPrefix); + } + return $aResult; + } + + /** + * @param array $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Charset.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Charset.php new file mode 100644 index 000000000..3ee0c3d04 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Charset.php @@ -0,0 +1,129 @@ + + */ + protected $aComments; + + /** + * @param string $sCharset + * @param int $iLineNo + */ + public function __construct($sCharset, $iLineNo = 0) + { + $this->sCharset = $sCharset; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param string $sCharset + * + * @return void + */ + public function setCharset($sCharset) + { + $this->sCharset = $sCharset; + } + + /** + * @return string + */ + public function getCharset() + { + return $this->sCharset; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return "@charset {$this->sCharset->render($oOutputFormat)};"; + } + + /** + * @return string + */ + public function atRuleName() + { + return 'charset'; + } + + /** + * @return string + */ + public function atRuleArgs() + { + return $this->sCharset; + } + + /** + * @param array $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Import.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Import.php new file mode 100644 index 000000000..a2253016b --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/Import.php @@ -0,0 +1,137 @@ + + */ + protected $aComments; + + /** + * @param URL $oLocation + * @param string $sMediaQuery + * @param int $iLineNo + */ + public function __construct(URL $oLocation, $sMediaQuery, $iLineNo = 0) + { + $this->oLocation = $oLocation; + $this->sMediaQuery = $sMediaQuery; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param URL $oLocation + * + * @return void + */ + public function setLocation($oLocation) + { + $this->oLocation = $oLocation; + } + + /** + * @return URL + */ + public function getLocation() + { + return $this->oLocation; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return "@import " . $this->oLocation->render($oOutputFormat) + . ($this->sMediaQuery === null ? '' : ' ' . $this->sMediaQuery) . ';'; + } + + /** + * @return string + */ + public function atRuleName() + { + return 'import'; + } + + /** + * @return array + */ + public function atRuleArgs() + { + $aResult = [$this->oLocation]; + if ($this->sMediaQuery) { + array_push($aResult, $this->sMediaQuery); + } + return $aResult; + } + + /** + * @param array $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php new file mode 100644 index 000000000..14ea5ebb7 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php @@ -0,0 +1,23 @@ +]* # any sequence of valid unescaped characters + (?:\\\\.)? # a single escaped character + (?:([\'"]).*?(?\~]+)[\w]+ # elements + | + \:{1,2}( # pseudo-elements + after|before|first-letter|first-line|selection + )) + /ix'; + + /** + * regexp for specificity calculations + * + * @var string + */ + const SELECTOR_VALIDATION_RX = '/ + ^( + (?: + [a-zA-Z0-9\x{00A0}-\x{FFFF}_^$|*="\'~\[\]()\-\s\.:#+>]* # any sequence of valid unescaped characters + (?:\\\\.)? # a single escaped character + (?:([\'"]).*?(?setSelector($sSelector); + if ($bCalculateSpecificity) { + $this->getSpecificity(); + } + } + + /** + * @return string + */ + public function getSelector() + { + return $this->sSelector; + } + + /** + * @param string $sSelector + * + * @return void + */ + public function setSelector($sSelector) + { + $this->sSelector = trim($sSelector); + $this->iSpecificity = null; + } + + /** + * @return string + */ + public function __toString() + { + return $this->getSelector(); + } + + /** + * @return int + */ + public function getSpecificity() + { + if ($this->iSpecificity === null) { + $a = 0; + /// @todo should exclude \# as well as "#" + $aMatches = null; + $b = substr_count($this->sSelector, '#'); + $c = preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $this->sSelector, $aMatches); + $d = preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $this->sSelector, $aMatches); + $this->iSpecificity = ($a * 1000) + ($b * 100) + ($c * 10) + $d; + } + return $this->iSpecificity; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Renderable.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Renderable.php new file mode 100644 index 000000000..dc1bff3c1 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Renderable.php @@ -0,0 +1,21 @@ + + */ + private $aIeHack; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @var int + */ + protected $iColNo; + + /** + * @var array + */ + protected $aComments; + + /** + * @param string $sRule + * @param int $iLineNo + * @param int $iColNo + */ + public function __construct($sRule, $iLineNo = 0, $iColNo = 0) + { + $this->sRule = $sRule; + $this->mValue = null; + $this->bIsImportant = false; + $this->aIeHack = []; + $this->iLineNo = $iLineNo; + $this->iColNo = $iColNo; + $this->aComments = []; + } + + /** + * @return Rule + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $aComments = $oParserState->consumeWhiteSpace(); + $oRule = new Rule( + $oParserState->parseIdentifier(!$oParserState->comes("--")), + $oParserState->currentLine(), + $oParserState->currentColumn() + ); + $oRule->setComments($aComments); + $oRule->addComments($oParserState->consumeWhiteSpace()); + $oParserState->consume(':'); + $oValue = Value::parseValue($oParserState, self::listDelimiterForRule($oRule->getRule())); + $oRule->setValue($oValue); + if ($oParserState->getSettings()->bLenientParsing) { + while ($oParserState->comes('\\')) { + $oParserState->consume('\\'); + $oRule->addIeHack($oParserState->consume()); + $oParserState->consumeWhiteSpace(); + } + } + $oParserState->consumeWhiteSpace(); + if ($oParserState->comes('!')) { + $oParserState->consume('!'); + $oParserState->consumeWhiteSpace(); + $oParserState->consume('important'); + $oRule->setIsImportant(true); + } + $oParserState->consumeWhiteSpace(); + while ($oParserState->comes(';')) { + $oParserState->consume(';'); + } + $oParserState->consumeWhiteSpace(); + + return $oRule; + } + + /** + * @param string $sRule + * + * @return array + */ + private static function listDelimiterForRule($sRule) + { + if (preg_match('/^font($|-)/', $sRule)) { + return [',', '/', ' ']; + } + return [',', ' ', '/']; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @return int + */ + public function getColNo() + { + return $this->iColNo; + } + + /** + * @param int $iLine + * @param int $iColumn + * + * @return void + */ + public function setPosition($iLine, $iColumn) + { + $this->iColNo = $iColumn; + $this->iLineNo = $iLine; + } + + /** + * @param string $sRule + * + * @return void + */ + public function setRule($sRule) + { + $this->sRule = $sRule; + } + + /** + * @return string + */ + public function getRule() + { + return $this->sRule; + } + + /** + * @return RuleValueList|null + */ + public function getValue() + { + return $this->mValue; + } + + /** + * @param RuleValueList|null $mValue + * + * @return void + */ + public function setValue($mValue) + { + $this->mValue = $mValue; + } + + /** + * @param array> $aSpaceSeparatedValues + * + * @return RuleValueList + * + * @deprecated will be removed in version 9.0 + * Old-Style 2-dimensional array given. Retained for (some) backwards-compatibility. + * Use `setValue()` instead and wrap the value inside a RuleValueList if necessary. + */ + public function setValues(array $aSpaceSeparatedValues) + { + $oSpaceSeparatedList = null; + if (count($aSpaceSeparatedValues) > 1) { + $oSpaceSeparatedList = new RuleValueList(' ', $this->iLineNo); + } + foreach ($aSpaceSeparatedValues as $aCommaSeparatedValues) { + $oCommaSeparatedList = null; + if (count($aCommaSeparatedValues) > 1) { + $oCommaSeparatedList = new RuleValueList(',', $this->iLineNo); + } + foreach ($aCommaSeparatedValues as $mValue) { + if (!$oSpaceSeparatedList && !$oCommaSeparatedList) { + $this->mValue = $mValue; + return $mValue; + } + if ($oCommaSeparatedList) { + $oCommaSeparatedList->addListComponent($mValue); + } else { + $oSpaceSeparatedList->addListComponent($mValue); + } + } + if (!$oSpaceSeparatedList) { + $this->mValue = $oCommaSeparatedList; + return $oCommaSeparatedList; + } else { + $oSpaceSeparatedList->addListComponent($oCommaSeparatedList); + } + } + $this->mValue = $oSpaceSeparatedList; + return $oSpaceSeparatedList; + } + + /** + * @return array> + * + * @deprecated will be removed in version 9.0 + * Old-Style 2-dimensional array returned. Retained for (some) backwards-compatibility. + * Use `getValue()` instead and check for the existence of a (nested set of) ValueList object(s). + */ + public function getValues() + { + if (!$this->mValue instanceof RuleValueList) { + return [[$this->mValue]]; + } + if ($this->mValue->getListSeparator() === ',') { + return [$this->mValue->getListComponents()]; + } + $aResult = []; + foreach ($this->mValue->getListComponents() as $mValue) { + if (!$mValue instanceof RuleValueList || $mValue->getListSeparator() !== ',') { + $aResult[] = [$mValue]; + continue; + } + if ($this->mValue->getListSeparator() === ' ' || count($aResult) === 0) { + $aResult[] = []; + } + foreach ($mValue->getListComponents() as $mValue) { + $aResult[count($aResult) - 1][] = $mValue; + } + } + return $aResult; + } + + /** + * Adds a value to the existing value. Value will be appended if a `RuleValueList` exists of the given type. + * Otherwise, the existing value will be wrapped by one. + * + * @param RuleValueList|array $mValue + * @param string $sType + * + * @return void + */ + public function addValue($mValue, $sType = ' ') + { + if (!is_array($mValue)) { + $mValue = [$mValue]; + } + if (!$this->mValue instanceof RuleValueList || $this->mValue->getListSeparator() !== $sType) { + $mCurrentValue = $this->mValue; + $this->mValue = new RuleValueList($sType, $this->iLineNo); + if ($mCurrentValue) { + $this->mValue->addListComponent($mCurrentValue); + } + } + foreach ($mValue as $mValueItem) { + $this->mValue->addListComponent($mValueItem); + } + } + + /** + * @param int $iModifier + * + * @return void + */ + public function addIeHack($iModifier) + { + $this->aIeHack[] = $iModifier; + } + + /** + * @param array $aModifiers + * + * @return void + */ + public function setIeHack(array $aModifiers) + { + $this->aIeHack = $aModifiers; + } + + /** + * @return array + */ + public function getIeHack() + { + return $this->aIeHack; + } + + /** + * @param bool $bIsImportant + * + * @return void + */ + public function setIsImportant($bIsImportant) + { + $this->bIsImportant = $bIsImportant; + } + + /** + * @return bool + */ + public function getIsImportant() + { + return $this->bIsImportant; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}"; + if ($this->mValue instanceof Value) { //Can also be a ValueList + $sResult .= $this->mValue->render($oOutputFormat); + } else { + $sResult .= $this->mValue; + } + if (!empty($this->aIeHack)) { + $sResult .= ' \\' . implode('\\', $this->aIeHack); + } + if ($this->bIsImportant) { + $sResult .= ' !important'; + } + $sResult .= ';'; + return $sResult; + } + + /** + * @param array $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php new file mode 100644 index 000000000..88bc5bd31 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php @@ -0,0 +1,73 @@ +sType = $sType; + $this->sArgs = $sArgs; + } + + /** + * @return string + */ + public function atRuleName() + { + return $this->sType; + } + + /** + * @return string + */ + public function atRuleArgs() + { + return $this->sArgs; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sArgs = $this->sArgs; + if ($sArgs) { + $sArgs = ' ' . $sArgs; + } + $sResult = "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{"; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + return $sResult; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php new file mode 100644 index 000000000..c27cdd4c8 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php @@ -0,0 +1,831 @@ + + */ + private $aSelectors; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + parent::__construct($iLineNo); + $this->aSelectors = []; + } + + /** + * @param CSSList|null $oList + * + * @return DeclarationBlock|false + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parse(ParserState $oParserState, $oList = null) + { + $aComments = []; + $oResult = new DeclarationBlock($oParserState->currentLine()); + try { + $aSelectorParts = []; + $sStringWrapperChar = false; + do { + $aSelectorParts[] = $oParserState->consume(1) + . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments); + if (in_array($oParserState->peek(), ['\'', '"']) && substr(end($aSelectorParts), -1) != "\\") { + if ($sStringWrapperChar === false) { + $sStringWrapperChar = $oParserState->peek(); + } elseif ($sStringWrapperChar == $oParserState->peek()) { + $sStringWrapperChar = false; + } + } + } while (!in_array($oParserState->peek(), ['{', '}']) || $sStringWrapperChar !== false); + $oResult->setSelectors(implode('', $aSelectorParts), $oList); + if ($oParserState->comes('{')) { + $oParserState->consume(1); + } + } catch (UnexpectedTokenException $e) { + if ($oParserState->getSettings()->bLenientParsing) { + if (!$oParserState->comes('}')) { + $oParserState->consumeUntil('}', false, true); + } + return false; + } else { + throw $e; + } + } + $oResult->setComments($aComments); + RuleSet::parseRuleSet($oParserState, $oResult); + return $oResult; + } + + /** + * @param array|string $mSelector + * @param CSSList|null $oList + * + * @throws UnexpectedTokenException + */ + public function setSelectors($mSelector, $oList = null) + { + if (is_array($mSelector)) { + $this->aSelectors = $mSelector; + } else { + $this->aSelectors = explode(',', $mSelector); + } + foreach ($this->aSelectors as $iKey => $mSelector) { + if (!($mSelector instanceof Selector)) { + if ($oList === null || !($oList instanceof KeyFrame)) { + if (!Selector::isValid($mSelector)) { + throw new UnexpectedTokenException( + "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.", + $mSelector, + "custom" + ); + } + $this->aSelectors[$iKey] = new Selector($mSelector); + } else { + if (!KeyframeSelector::isValid($mSelector)) { + throw new UnexpectedTokenException( + "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.", + $mSelector, + "custom" + ); + } + $this->aSelectors[$iKey] = new KeyframeSelector($mSelector); + } + } + } + } + + /** + * Remove one of the selectors of the block. + * + * @param Selector|string $mSelector + * + * @return bool + */ + public function removeSelector($mSelector) + { + if ($mSelector instanceof Selector) { + $mSelector = $mSelector->getSelector(); + } + foreach ($this->aSelectors as $iKey => $oSelector) { + if ($oSelector->getSelector() === $mSelector) { + unset($this->aSelectors[$iKey]); + return true; + } + } + return false; + } + + /** + * @return array + * + * @deprecated will be removed in version 9.0; use `getSelectors()` instead + */ + public function getSelector() + { + return $this->getSelectors(); + } + + /** + * @param Selector|string $mSelector + * @param CSSList|null $oList + * + * @return void + * + * @deprecated will be removed in version 9.0; use `setSelectors()` instead + */ + public function setSelector($mSelector, $oList = null) + { + $this->setSelectors($mSelector, $oList); + } + + /** + * @return array + */ + public function getSelectors() + { + return $this->aSelectors; + } + + /** + * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts. + * + * @return void + */ + public function expandShorthands() + { + // border must be expanded before dimensions + $this->expandBorderShorthand(); + $this->expandDimensionsShorthand(); + $this->expandFontShorthand(); + $this->expandBackgroundShorthand(); + $this->expandListStyleShorthand(); + } + + /** + * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible. + * + * @return void + */ + public function createShorthands() + { + $this->createBackgroundShorthand(); + $this->createDimensionsShorthand(); + // border must be shortened after dimensions + $this->createBorderShorthand(); + $this->createFontShorthand(); + $this->createListStyleShorthand(); + } + + /** + * Splits shorthand border declarations (e.g. `border: 1px red;`). + * + * Additional splitting happens in expandDimensionsShorthand. + * + * Multiple borders are not yet supported as of 3. + * + * @return void + */ + public function expandBorderShorthand() + { + $aBorderRules = [ + 'border', + 'border-left', + 'border-right', + 'border-top', + 'border-bottom', + ]; + $aBorderSizes = [ + 'thin', + 'medium', + 'thick', + ]; + $aRules = $this->getRulesAssoc(); + foreach ($aBorderRules as $sBorderRule) { + if (!isset($aRules[$sBorderRule])) { + continue; + } + $oRule = $aRules[$sBorderRule]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + foreach ($aValues as $mValue) { + if ($mValue instanceof Value) { + $mNewValue = clone $mValue; + } else { + $mNewValue = $mValue; + } + if ($mValue instanceof Size) { + $sNewRuleName = $sBorderRule . "-width"; + } elseif ($mValue instanceof Color) { + $sNewRuleName = $sBorderRule . "-color"; + } else { + if (in_array($mValue, $aBorderSizes)) { + $sNewRuleName = $sBorderRule . "-width"; + } else { + $sNewRuleName = $sBorderRule . "-style"; + } + } + $oNewRule = new Rule($sNewRuleName, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue([$mNewValue]); + $this->addRule($oNewRule); + } + $this->removeRule($sBorderRule); + } + } + + /** + * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`) + * into their constituent parts. + * + * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`. + * + * @return void + */ + public function expandDimensionsShorthand() + { + $aExpansions = [ + 'margin' => 'margin-%s', + 'padding' => 'padding-%s', + 'border-color' => 'border-%s-color', + 'border-style' => 'border-%s-style', + 'border-width' => 'border-%s-width', + ]; + $aRules = $this->getRulesAssoc(); + foreach ($aExpansions as $sProperty => $sExpanded) { + if (!isset($aRules[$sProperty])) { + continue; + } + $oRule = $aRules[$sProperty]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + $top = $right = $bottom = $left = null; + switch (count($aValues)) { + case 1: + $top = $right = $bottom = $left = $aValues[0]; + break; + case 2: + $top = $bottom = $aValues[0]; + $left = $right = $aValues[1]; + break; + case 3: + $top = $aValues[0]; + $left = $right = $aValues[1]; + $bottom = $aValues[2]; + break; + case 4: + $top = $aValues[0]; + $right = $aValues[1]; + $bottom = $aValues[2]; + $left = $aValues[3]; + break; + } + foreach (['top', 'right', 'bottom', 'left'] as $sPosition) { + $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue(${$sPosition}); + $this->addRule($oNewRule); + } + $this->removeRule($sProperty); + } + } + + /** + * Converts shorthand font declarations + * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`) + * into their constituent parts. + * + * @return void + */ + public function expandFontShorthand() + { + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['font'])) { + return; + } + $oRule = $aRules['font']; + // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand + $aFontProperties = [ + 'font-style' => 'normal', + 'font-variant' => 'normal', + 'font-weight' => 'normal', + 'font-size' => 'normal', + 'line-height' => 'normal', + ]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + foreach ($aValues as $mValue) { + if (!$mValue instanceof Value) { + $mValue = mb_strtolower($mValue); + } + if (in_array($mValue, ['normal', 'inherit'])) { + foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) { + if (!isset($aFontProperties[$sProperty])) { + $aFontProperties[$sProperty] = $mValue; + } + } + } elseif (in_array($mValue, ['italic', 'oblique'])) { + $aFontProperties['font-style'] = $mValue; + } elseif ($mValue == 'small-caps') { + $aFontProperties['font-variant'] = $mValue; + } elseif ( + in_array($mValue, ['bold', 'bolder', 'lighter']) + || ($mValue instanceof Size + && in_array($mValue->getSize(), range(100, 900, 100))) + ) { + $aFontProperties['font-weight'] = $mValue; + } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') { + list($oSize, $oHeight) = $mValue->getListComponents(); + $aFontProperties['font-size'] = $oSize; + $aFontProperties['line-height'] = $oHeight; + } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) { + $aFontProperties['font-size'] = $mValue; + } else { + $aFontProperties['font-family'] = $mValue; + } + } + foreach ($aFontProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->addValue($mValue); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $this->addRule($oNewRule); + } + $this->removeRule('font'); + } + + /** + * Converts shorthand background declarations + * (e.g. `background: url("chess.png") gray 50% repeat fixed;`) + * into their constituent parts. + * + * @see http://www.w3.org/TR/21/colors.html#propdef-background + * + * @return void + */ + public function expandBackgroundShorthand() + { + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['background'])) { + return; + } + $oRule = $aRules['background']; + $aBgProperties = [ + 'background-color' => ['transparent'], + 'background-image' => ['none'], + 'background-repeat' => ['repeat'], + 'background-attachment' => ['scroll'], + 'background-position' => [ + new Size(0, '%', null, false, $this->iLineNo), + new Size(0, '%', null, false, $this->iLineNo), + ], + ]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + if (count($aValues) == 1 && $aValues[0] == 'inherit') { + foreach ($aBgProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->addValue('inherit'); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $this->addRule($oNewRule); + } + $this->removeRule('background'); + return; + } + $iNumBgPos = 0; + foreach ($aValues as $mValue) { + if (!$mValue instanceof Value) { + $mValue = mb_strtolower($mValue); + } + if ($mValue instanceof URL) { + $aBgProperties['background-image'] = $mValue; + } elseif ($mValue instanceof Color) { + $aBgProperties['background-color'] = $mValue; + } elseif (in_array($mValue, ['scroll', 'fixed'])) { + $aBgProperties['background-attachment'] = $mValue; + } elseif (in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'])) { + $aBgProperties['background-repeat'] = $mValue; + } elseif ( + in_array($mValue, ['left', 'center', 'right', 'top', 'bottom']) + || $mValue instanceof Size + ) { + if ($iNumBgPos == 0) { + $aBgProperties['background-position'][0] = $mValue; + $aBgProperties['background-position'][1] = 'center'; + } else { + $aBgProperties['background-position'][$iNumBgPos] = $mValue; + } + $iNumBgPos++; + } + } + foreach ($aBgProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue($mValue); + $this->addRule($oNewRule); + } + $this->removeRule('background'); + } + + /** + * @return void + */ + public function expandListStyleShorthand() + { + $aListProperties = [ + 'list-style-type' => 'disc', + 'list-style-position' => 'outside', + 'list-style-image' => 'none', + ]; + $aListStyleTypes = [ + 'none', + 'disc', + 'circle', + 'square', + 'decimal-leading-zero', + 'decimal', + 'lower-roman', + 'upper-roman', + 'lower-greek', + 'lower-alpha', + 'lower-latin', + 'upper-alpha', + 'upper-latin', + 'hebrew', + 'armenian', + 'georgian', + 'cjk-ideographic', + 'hiragana', + 'hira-gana-iroha', + 'katakana-iroha', + 'katakana', + ]; + $aListStylePositions = [ + 'inside', + 'outside', + ]; + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['list-style'])) { + return; + } + $oRule = $aRules['list-style']; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + if (count($aValues) == 1 && $aValues[0] == 'inherit') { + foreach ($aListProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->addValue('inherit'); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $this->addRule($oNewRule); + } + $this->removeRule('list-style'); + return; + } + foreach ($aValues as $mValue) { + if (!$mValue instanceof Value) { + $mValue = mb_strtolower($mValue); + } + if ($mValue instanceof Url) { + $aListProperties['list-style-image'] = $mValue; + } elseif (in_array($mValue, $aListStyleTypes)) { + $aListProperties['list-style-types'] = $mValue; + } elseif (in_array($mValue, $aListStylePositions)) { + $aListProperties['list-style-position'] = $mValue; + } + } + foreach ($aListProperties as $sProperty => $mValue) { + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + $oNewRule->setIsImportant($oRule->getIsImportant()); + $oNewRule->addValue($mValue); + $this->addRule($oNewRule); + } + $this->removeRule('list-style'); + } + + /** + * @param array $aProperties + * @param string $sShorthand + * + * @return void + */ + public function createShorthandProperties(array $aProperties, $sShorthand) + { + $aRules = $this->getRulesAssoc(); + $aNewValues = []; + foreach ($aProperties as $sProperty) { + if (!isset($aRules[$sProperty])) { + continue; + } + $oRule = $aRules[$sProperty]; + if (!$oRule->getIsImportant()) { + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + foreach ($aValues as $mValue) { + $aNewValues[] = $mValue; + } + $this->removeRule($sProperty); + } + } + if (count($aNewValues)) { + $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo()); + foreach ($aNewValues as $mValue) { + $oNewRule->addValue($mValue); + } + $this->addRule($oNewRule); + } + } + + /** + * @return void + */ + public function createBackgroundShorthand() + { + $aProperties = [ + 'background-color', + 'background-image', + 'background-repeat', + 'background-position', + 'background-attachment', + ]; + $this->createShorthandProperties($aProperties, 'background'); + } + + /** + * @return void + */ + public function createListStyleShorthand() + { + $aProperties = [ + 'list-style-type', + 'list-style-position', + 'list-style-image', + ]; + $this->createShorthandProperties($aProperties, 'list-style'); + } + + /** + * Combines `border-color`, `border-style` and `border-width` into `border`. + * + * Should be run after `create_dimensions_shorthand`! + * + * @return void + */ + public function createBorderShorthand() + { + $aProperties = [ + 'border-width', + 'border-style', + 'border-color', + ]; + $this->createShorthandProperties($aProperties, 'border'); + } + + /** + * Looks for long format CSS dimensional properties + * (margin, padding, border-color, border-style and border-width) + * and converts them into shorthand CSS properties. + * + * @return void + */ + public function createDimensionsShorthand() + { + $aPositions = ['top', 'right', 'bottom', 'left']; + $aExpansions = [ + 'margin' => 'margin-%s', + 'padding' => 'padding-%s', + 'border-color' => 'border-%s-color', + 'border-style' => 'border-%s-style', + 'border-width' => 'border-%s-width', + ]; + $aRules = $this->getRulesAssoc(); + foreach ($aExpansions as $sProperty => $sExpanded) { + $aFoldable = []; + foreach ($aRules as $sRuleName => $oRule) { + foreach ($aPositions as $sPosition) { + if ($sRuleName == sprintf($sExpanded, $sPosition)) { + $aFoldable[$sRuleName] = $oRule; + } + } + } + // All four dimensions must be present + if (count($aFoldable) == 4) { + $aValues = []; + foreach ($aPositions as $sPosition) { + $oRule = $aRules[sprintf($sExpanded, $sPosition)]; + $mRuleValue = $oRule->getValue(); + $aRuleValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aRuleValues[] = $mRuleValue; + } else { + $aRuleValues = $mRuleValue->getListComponents(); + } + $aValues[$sPosition] = $aRuleValues; + } + $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo()); + if ((string)$aValues['left'][0] == (string)$aValues['right'][0]) { + if ((string)$aValues['top'][0] == (string)$aValues['bottom'][0]) { + if ((string)$aValues['top'][0] == (string)$aValues['left'][0]) { + // All 4 sides are equal + $oNewRule->addValue($aValues['top']); + } else { + // Top and bottom are equal, left and right are equal + $oNewRule->addValue($aValues['top']); + $oNewRule->addValue($aValues['left']); + } + } else { + // Only left and right are equal + $oNewRule->addValue($aValues['top']); + $oNewRule->addValue($aValues['left']); + $oNewRule->addValue($aValues['bottom']); + } + } else { + // No sides are equal + $oNewRule->addValue($aValues['top']); + $oNewRule->addValue($aValues['left']); + $oNewRule->addValue($aValues['bottom']); + $oNewRule->addValue($aValues['right']); + } + $this->addRule($oNewRule); + foreach ($aPositions as $sPosition) { + $this->removeRule(sprintf($sExpanded, $sPosition)); + } + } + } + } + + /** + * Looks for long format CSS font properties (e.g. `font-weight`) and + * tries to convert them into a shorthand CSS `font` property. + * + * At least `font-size` AND `font-family` must be present in order to create a shorthand declaration. + * + * @return void + */ + public function createFontShorthand() + { + $aFontProperties = [ + 'font-style', + 'font-variant', + 'font-weight', + 'font-size', + 'line-height', + 'font-family', + ]; + $aRules = $this->getRulesAssoc(); + if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) { + return; + } + $oOldRule = isset($aRules['font-size']) ? $aRules['font-size'] : $aRules['font-family']; + $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo()); + unset($oOldRule); + foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) { + if (isset($aRules[$sProperty])) { + $oRule = $aRules[$sProperty]; + $mRuleValue = $oRule->getValue(); + $aValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aValues[] = $mRuleValue; + } else { + $aValues = $mRuleValue->getListComponents(); + } + if ($aValues[0] !== 'normal') { + $oNewRule->addValue($aValues[0]); + } + } + } + // Get the font-size value + $oRule = $aRules['font-size']; + $mRuleValue = $oRule->getValue(); + $aFSValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aFSValues[] = $mRuleValue; + } else { + $aFSValues = $mRuleValue->getListComponents(); + } + // But wait to know if we have line-height to add it + if (isset($aRules['line-height'])) { + $oRule = $aRules['line-height']; + $mRuleValue = $oRule->getValue(); + $aLHValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aLHValues[] = $mRuleValue; + } else { + $aLHValues = $mRuleValue->getListComponents(); + } + if ($aLHValues[0] !== 'normal') { + $val = new RuleValueList('/', $this->iLineNo); + $val->addListComponent($aFSValues[0]); + $val->addListComponent($aLHValues[0]); + $oNewRule->addValue($val); + } + } else { + $oNewRule->addValue($aFSValues[0]); + } + $oRule = $aRules['font-family']; + $mRuleValue = $oRule->getValue(); + $aFFValues = []; + if (!$mRuleValue instanceof RuleValueList) { + $aFFValues[] = $mRuleValue; + } else { + $aFFValues = $mRuleValue->getListComponents(); + } + $oFFValue = new RuleValueList(',', $this->iLineNo); + $oFFValue->setListComponents($aFFValues); + $oNewRule->addValue($oFFValue); + + $this->addRule($oNewRule); + foreach ($aFontProperties as $sProperty) { + $this->removeRule($sProperty); + } + } + + /** + * @return string + * + * @throws OutputException + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + * + * @throws OutputException + */ + public function render(OutputFormat $oOutputFormat) + { + if (count($this->aSelectors) === 0) { + // If all the selectors have been removed, this declaration block becomes invalid + throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo); + } + $sResult = $oOutputFormat->sBeforeDeclarationBlock; + $sResult .= $oOutputFormat->implode( + $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(), + $this->aSelectors + ); + $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors; + $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{'; + $sResult .= parent::render($oOutputFormat); + $sResult .= '}'; + $sResult .= $oOutputFormat->sAfterDeclarationBlock; + return $sResult; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php new file mode 100644 index 000000000..9404bb0bd --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php @@ -0,0 +1,326 @@ + + */ + private $aRules; + + /** + * @var int + */ + protected $iLineNo; + + /** + * @var array + */ + protected $aComments; + + /** + * @param int $iLineNo + */ + public function __construct($iLineNo = 0) + { + $this->aRules = []; + $this->iLineNo = $iLineNo; + $this->aComments = []; + } + + /** + * @return void + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parseRuleSet(ParserState $oParserState, RuleSet $oRuleSet) + { + while ($oParserState->comes(';')) { + $oParserState->consume(';'); + } + while (!$oParserState->comes('}')) { + $oRule = null; + if ($oParserState->getSettings()->bLenientParsing) { + try { + $oRule = Rule::parse($oParserState); + } catch (UnexpectedTokenException $e) { + try { + $sConsume = $oParserState->consumeUntil(["\n", ";", '}'], true); + // We need to “unfind” the matches to the end of the ruleSet as this will be matched later + if ($oParserState->streql(substr($sConsume, -1), '}')) { + $oParserState->backtrack(1); + } else { + while ($oParserState->comes(';')) { + $oParserState->consume(';'); + } + } + } catch (UnexpectedTokenException $e) { + // We’ve reached the end of the document. Just close the RuleSet. + return; + } + } + } else { + $oRule = Rule::parse($oParserState); + } + if ($oRule) { + $oRuleSet->addRule($oRule); + } + } + $oParserState->consume('}'); + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } + + /** + * @param Rule|null $oSibling + * + * @return void + */ + public function addRule(Rule $oRule, Rule $oSibling = null) + { + $sRule = $oRule->getRule(); + if (!isset($this->aRules[$sRule])) { + $this->aRules[$sRule] = []; + } + + $iPosition = count($this->aRules[$sRule]); + + if ($oSibling !== null) { + $iSiblingPos = array_search($oSibling, $this->aRules[$sRule], true); + if ($iSiblingPos !== false) { + $iPosition = $iSiblingPos; + $oRule->setPosition($oSibling->getLineNo(), $oSibling->getColNo() - 1); + } + } + if ($oRule->getLineNo() === 0 && $oRule->getColNo() === 0) { + //this node is added manually, give it the next best line + $rules = $this->getRules(); + $pos = count($rules); + if ($pos > 0) { + $last = $rules[$pos - 1]; + $oRule->setPosition($last->getLineNo() + 1, 0); + } + } + + array_splice($this->aRules[$sRule], $iPosition, 0, [$oRule]); + } + + /** + * Returns all rules matching the given rule name + * + * @example $oRuleSet->getRules('font') // returns array(0 => $oRule, …) or array(). + * + * @example $oRuleSet->getRules('font-') + * //returns an array of all rules either beginning with font- or matching font. + * + * @param Rule|string|null $mRule + * Pattern to search for. If null, returns all rules. + * If the pattern ends with a dash, all rules starting with the pattern are returned + * as well as one matching the pattern with the dash excluded. + * Passing a Rule behaves like calling `getRules($mRule->getRule())`. + * + * @return array + */ + public function getRules($mRule = null) + { + if ($mRule instanceof Rule) { + $mRule = $mRule->getRule(); + } + /** @var array $aResult */ + $aResult = []; + foreach ($this->aRules as $sName => $aRules) { + // Either no search rule is given or the search rule matches the found rule exactly + // or the search rule ends in “-” and the found rule starts with the search rule. + if ( + !$mRule || $sName === $mRule + || ( + strrpos($mRule, '-') === strlen($mRule) - strlen('-') + && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1)) + ) + ) { + $aResult = array_merge($aResult, $aRules); + } + } + usort($aResult, function (Rule $first, Rule $second) { + if ($first->getLineNo() === $second->getLineNo()) { + return $first->getColNo() - $second->getColNo(); + } + return $first->getLineNo() - $second->getLineNo(); + }); + return $aResult; + } + + /** + * Overrides all the rules of this set. + * + * @param array $aRules The rules to override with. + * + * @return void + */ + public function setRules(array $aRules) + { + $this->aRules = []; + foreach ($aRules as $rule) { + $this->addRule($rule); + } + } + + /** + * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name + * as keys. This method exists mainly for backwards-compatibility and is really only partially useful. + * + * Note: This method loses some information: Calling this (with an argument of `background-`) on a declaration block + * like `{ background-color: green; background-color; rgba(0, 127, 0, 0.7); }` will only yield an associative array + * containing the rgba-valued rule while `getRules()` would yield an indexed array containing both. + * + * @param Rule|string|null $mRule $mRule + * Pattern to search for. If null, returns all rules. If the pattern ends with a dash, + * all rules starting with the pattern are returned as well as one matching the pattern with the dash + * excluded. Passing a Rule behaves like calling `getRules($mRule->getRule())`. + * + * @return array + */ + public function getRulesAssoc($mRule = null) + { + /** @var array $aResult */ + $aResult = []; + foreach ($this->getRules($mRule) as $oRule) { + $aResult[$oRule->getRule()] = $oRule; + } + return $aResult; + } + + /** + * Removes a rule from this RuleSet. This accepts all the possible values that `getRules()` accepts. + * + * If given a Rule, it will only remove this particular rule (by identity). + * If given a name, it will remove all rules by that name. + * + * Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would + * remove all rules with the same name. To get the old behaviour, use `removeRule($oRule->getRule())`. + * + * @param Rule|string|null $mRule + * pattern to remove. If $mRule is null, all rules are removed. If the pattern ends in a dash, + * all rules starting with the pattern are removed as well as one matching the pattern with the dash + * excluded. Passing a Rule behaves matches by identity. + * + * @return void + */ + public function removeRule($mRule) + { + if ($mRule instanceof Rule) { + $sRule = $mRule->getRule(); + if (!isset($this->aRules[$sRule])) { + return; + } + foreach ($this->aRules[$sRule] as $iKey => $oRule) { + if ($oRule === $mRule) { + unset($this->aRules[$sRule][$iKey]); + } + } + } else { + foreach ($this->aRules as $sName => $aRules) { + // Either no search rule is given or the search rule matches the found rule exactly + // or the search rule ends in “-” and the found rule starts with the search rule or equals it + // (without the trailing dash). + if ( + !$mRule || $sName === $mRule + || (strrpos($mRule, '-') === strlen($mRule) - strlen('-') + && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1))) + ) { + unset($this->aRules[$sName]); + } + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sResult = ''; + $bIsFirst = true; + foreach ($this->aRules as $aRules) { + foreach ($aRules as $oRule) { + $sRendered = $oOutputFormat->safely(function () use ($oRule, $oOutputFormat) { + return $oRule->render($oOutputFormat->nextLevel()); + }); + if ($sRendered === null) { + continue; + } + if ($bIsFirst) { + $bIsFirst = false; + $sResult .= $oOutputFormat->nextLevel()->spaceBeforeRules(); + } else { + $sResult .= $oOutputFormat->nextLevel()->spaceBetweenRules(); + } + $sResult .= $sRendered; + } + } + + if (!$bIsFirst) { + // Had some output + $sResult .= $oOutputFormat->spaceAfterRules(); + } + + return $oOutputFormat->removeLastSemicolon($sResult); + } + + /** + * @param array $aComments + * + * @return void + */ + public function addComments(array $aComments) + { + $this->aComments = array_merge($this->aComments, $aComments); + } + + /** + * @return array + */ + public function getComments() + { + return $this->aComments; + } + + /** + * @param array $aComments + * + * @return void + */ + public function setComments(array $aComments) + { + $this->aComments = $aComments; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Settings.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Settings.php new file mode 100644 index 000000000..7b8580962 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Settings.php @@ -0,0 +1,89 @@ +bMultibyteSupport = extension_loaded('mbstring'); + } + + /** + * @return self new instance + */ + public static function create() + { + return new Settings(); + } + + /** + * @param bool $bMultibyteSupport + * + * @return self fluent interface + */ + public function withMultibyteSupport($bMultibyteSupport = true) + { + $this->bMultibyteSupport = $bMultibyteSupport; + return $this; + } + + /** + * @param string $sDefaultCharset + * + * @return self fluent interface + */ + public function withDefaultCharset($sDefaultCharset) + { + $this->sDefaultCharset = $sDefaultCharset; + return $this; + } + + /** + * @param bool $bLenientParsing + * + * @return self fluent interface + */ + public function withLenientParsing($bLenientParsing = true) + { + $this->bLenientParsing = $bLenientParsing; + return $this; + } + + /** + * @return self fluent interface + */ + public function beStrict() + { + return $this->withLenientParsing(false); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php new file mode 100644 index 000000000..e6b8c1189 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php @@ -0,0 +1,73 @@ + $aArguments + * @param string $sSeparator + * @param int $iLineNo + */ + public function __construct($sName, $aArguments, $sSeparator = ',', $iLineNo = 0) + { + if ($aArguments instanceof RuleValueList) { + $sSeparator = $aArguments->getListSeparator(); + $aArguments = $aArguments->getListComponents(); + } + $this->sName = $sName; + $this->iLineNo = $iLineNo; + parent::__construct($aArguments, $sSeparator, $iLineNo); + } + + /** + * @return string + */ + public function getName() + { + return $this->sName; + } + + /** + * @param string $sName + * + * @return void + */ + public function setName($sName) + { + $this->sName = $sName; + } + + /** + * @return array + */ + public function getArguments() + { + return $this->aComponents; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $aArguments = parent::render($oOutputFormat); + return "{$this->sName}({$aArguments})"; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSString.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSString.php new file mode 100644 index 000000000..9fafedd7a --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CSSString.php @@ -0,0 +1,105 @@ +sString = $sString; + parent::__construct($iLineNo); + } + + /** + * @return CSSString + * + * @throws SourceException + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $sBegin = $oParserState->peek(); + $sQuote = null; + if ($sBegin === "'") { + $sQuote = "'"; + } elseif ($sBegin === '"') { + $sQuote = '"'; + } + if ($sQuote !== null) { + $oParserState->consume($sQuote); + } + $sResult = ""; + $sContent = null; + if ($sQuote === null) { + // Unquoted strings end in whitespace or with braces, brackets, parentheses + while (!preg_match('/[\\s{}()<>\\[\\]]/isu', $oParserState->peek())) { + $sResult .= $oParserState->parseCharacter(false); + } + } else { + while (!$oParserState->comes($sQuote)) { + $sContent = $oParserState->parseCharacter(false); + if ($sContent === null) { + throw new SourceException( + "Non-well-formed quoted string {$oParserState->peek(3)}", + $oParserState->currentLine() + ); + } + $sResult .= $sContent; + } + $oParserState->consume($sQuote); + } + return new CSSString($sResult, $oParserState->currentLine()); + } + + /** + * @param string $sString + * + * @return void + */ + public function setString($sString) + { + $this->sString = $sString; + } + + /** + * @return string + */ + public function getString() + { + return $this->sString; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $sString = addslashes($this->sString); + $sString = str_replace("\n", '\A', $sString); + return $oOutputFormat->getStringQuotingType() . $sString . $oOutputFormat->getStringQuotingType(); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php new file mode 100644 index 000000000..5c92e0c08 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php @@ -0,0 +1,89 @@ +consumeUntil('(', false, true)); + $oCalcList = new CalcRuleValueList($oParserState->currentLine()); + $oList = new RuleValueList(',', $oParserState->currentLine()); + $iNestingLevel = 0; + $iLastComponentType = null; + while (!$oParserState->comes(')') || $iNestingLevel > 0) { + $oParserState->consumeWhiteSpace(); + if ($oParserState->comes('(')) { + $iNestingLevel++; + $oCalcList->addListComponent($oParserState->consume(1)); + $oParserState->consumeWhiteSpace(); + continue; + } elseif ($oParserState->comes(')')) { + $iNestingLevel--; + $oCalcList->addListComponent($oParserState->consume(1)); + $oParserState->consumeWhiteSpace(); + continue; + } + if ($iLastComponentType != CalcFunction::T_OPERAND) { + $oVal = Value::parsePrimitiveValue($oParserState); + $oCalcList->addListComponent($oVal); + $iLastComponentType = CalcFunction::T_OPERAND; + } else { + if (in_array($oParserState->peek(), $aOperators)) { + if (($oParserState->comes('-') || $oParserState->comes('+'))) { + if ( + $oParserState->peek(1, -1) != ' ' + || !($oParserState->comes('- ') + || $oParserState->comes('+ ')) + ) { + throw new UnexpectedTokenException( + " {$oParserState->peek()} ", + $oParserState->peek(1, -1) . $oParserState->peek(2), + 'literal', + $oParserState->currentLine() + ); + } + } + $oCalcList->addListComponent($oParserState->consume(1)); + $iLastComponentType = CalcFunction::T_OPERATOR; + } else { + throw new UnexpectedTokenException( + sprintf( + 'Next token was expected to be an operand of type %s. Instead "%s" was found.', + implode(', ', $aOperators), + $oVal + ), + '', + 'custom', + $oParserState->currentLine() + ); + } + } + $oParserState->consumeWhiteSpace(); + } + $oList->addListComponent($oCalcList); + $oParserState->consume(')'); + return new CalcFunction($sFunction, $oList, ',', $oParserState->currentLine()); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php new file mode 100644 index 000000000..7dbd26a1b --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php @@ -0,0 +1,24 @@ +implode(' ', $this->aComponents); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Color.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Color.php new file mode 100644 index 000000000..8dc52960c --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Color.php @@ -0,0 +1,166 @@ + $aColor + * @param int $iLineNo + */ + public function __construct(array $aColor, $iLineNo = 0) + { + parent::__construct(implode('', array_keys($aColor)), $aColor, ',', $iLineNo); + } + + /** + * @return Color|CSSFunction + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $aColor = []; + if ($oParserState->comes('#')) { + $oParserState->consume('#'); + $sValue = $oParserState->parseIdentifier(false); + if ($oParserState->strlen($sValue) === 3) { + $sValue = $sValue[0] . $sValue[0] . $sValue[1] . $sValue[1] . $sValue[2] . $sValue[2]; + } elseif ($oParserState->strlen($sValue) === 4) { + $sValue = $sValue[0] . $sValue[0] . $sValue[1] . $sValue[1] . $sValue[2] . $sValue[2] . $sValue[3] + . $sValue[3]; + } + + if ($oParserState->strlen($sValue) === 8) { + $aColor = [ + 'r' => new Size(intval($sValue[0] . $sValue[1], 16), null, true, $oParserState->currentLine()), + 'g' => new Size(intval($sValue[2] . $sValue[3], 16), null, true, $oParserState->currentLine()), + 'b' => new Size(intval($sValue[4] . $sValue[5], 16), null, true, $oParserState->currentLine()), + 'a' => new Size( + round(self::mapRange(intval($sValue[6] . $sValue[7], 16), 0, 255, 0, 1), 2), + null, + true, + $oParserState->currentLine() + ), + ]; + } else { + $aColor = [ + 'r' => new Size(intval($sValue[0] . $sValue[1], 16), null, true, $oParserState->currentLine()), + 'g' => new Size(intval($sValue[2] . $sValue[3], 16), null, true, $oParserState->currentLine()), + 'b' => new Size(intval($sValue[4] . $sValue[5], 16), null, true, $oParserState->currentLine()), + ]; + } + } else { + $sColorMode = $oParserState->parseIdentifier(true); + $oParserState->consumeWhiteSpace(); + $oParserState->consume('('); + + $bContainsVar = false; + $iLength = $oParserState->strlen($sColorMode); + for ($i = 0; $i < $iLength; ++$i) { + $oParserState->consumeWhiteSpace(); + if ($oParserState->comes('var')) { + $aColor[$sColorMode[$i]] = CSSFunction::parseIdentifierOrFunction($oParserState); + $bContainsVar = true; + } else { + $aColor[$sColorMode[$i]] = Size::parse($oParserState, true); + } + + if ($bContainsVar && $oParserState->comes(')')) { + // With a var argument the function can have fewer arguments + break; + } + + $oParserState->consumeWhiteSpace(); + if ($i < ($iLength - 1)) { + $oParserState->consume(','); + } + } + $oParserState->consume(')'); + + if ($bContainsVar) { + return new CSSFunction($sColorMode, array_values($aColor), ',', $oParserState->currentLine()); + } + } + return new Color($aColor, $oParserState->currentLine()); + } + + /** + * @param float $fVal + * @param float $fFromMin + * @param float $fFromMax + * @param float $fToMin + * @param float $fToMax + * + * @return float + */ + private static function mapRange($fVal, $fFromMin, $fFromMax, $fToMin, $fToMax) + { + $fFromRange = $fFromMax - $fFromMin; + $fToRange = $fToMax - $fToMin; + $fMultiplier = $fToRange / $fFromRange; + $fNewVal = $fVal - $fFromMin; + $fNewVal *= $fMultiplier; + return $fNewVal + $fToMin; + } + + /** + * @return array + */ + public function getColor() + { + return $this->aComponents; + } + + /** + * @param array $aColor + * + * @return void + */ + public function setColor(array $aColor) + { + $this->setName(implode('', array_keys($aColor))); + $this->aComponents = $aColor; + } + + /** + * @return string + */ + public function getColorDescription() + { + return $this->getName(); + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + // Shorthand RGB color values + if ($oOutputFormat->getRGBHashNotation() && implode('', array_keys($this->aComponents)) === 'rgb') { + $sResult = sprintf( + '%02x%02x%02x', + $this->aComponents['r']->getSize(), + $this->aComponents['g']->getSize(), + $this->aComponents['b']->getSize() + ); + return '#' . (($sResult[0] == $sResult[1]) && ($sResult[2] == $sResult[3]) && ($sResult[4] == $sResult[5]) + ? "$sResult[0]$sResult[2]$sResult[4]" : $sResult); + } + return parent::render($oOutputFormat); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/LineName.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/LineName.php new file mode 100644 index 000000000..e231ce38f --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/LineName.php @@ -0,0 +1,65 @@ + $aComponents + * @param int $iLineNo + */ + public function __construct(array $aComponents = [], $iLineNo = 0) + { + parent::__construct($aComponents, ' ', $iLineNo); + } + + /** + * @return LineName + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parse(ParserState $oParserState) + { + $oParserState->consume('['); + $oParserState->consumeWhiteSpace(); + $aNames = []; + do { + if ($oParserState->getSettings()->bLenientParsing) { + try { + $aNames[] = $oParserState->parseIdentifier(); + } catch (UnexpectedTokenException $e) { + if (!$oParserState->comes(']')) { + throw $e; + } + } + } else { + $aNames[] = $oParserState->parseIdentifier(); + } + $oParserState->consumeWhiteSpace(); + } while (!$oParserState->comes(']')); + $oParserState->consume(']'); + return new LineName($aNames, $oParserState->currentLine()); + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return '[' . parent::render(OutputFormat::createCompact()) . ']'; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php new file mode 100644 index 000000000..055a43975 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php @@ -0,0 +1,14 @@ + + */ + const ABSOLUTE_SIZE_UNITS = ['px', 'cm', 'mm', 'mozmm', 'in', 'pt', 'pc', 'vh', 'vw', 'vmin', 'vmax', 'rem']; + + /** + * @var array + */ + const RELATIVE_SIZE_UNITS = ['%', 'em', 'ex', 'ch', 'fr']; + + /** + * @var array + */ + const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turns', 'Hz', 'kHz']; + + /** + * @var array>|null + */ + private static $SIZE_UNITS = null; + + /** + * @var float + */ + private $fSize; + + /** + * @var string|null + */ + private $sUnit; + + /** + * @var bool + */ + private $bIsColorComponent; + + /** + * @param float|int|string $fSize + * @param string|null $sUnit + * @param bool $bIsColorComponent + * @param int $iLineNo + */ + public function __construct($fSize, $sUnit = null, $bIsColorComponent = false, $iLineNo = 0) + { + parent::__construct($iLineNo); + $this->fSize = (float)$fSize; + $this->sUnit = $sUnit; + $this->bIsColorComponent = $bIsColorComponent; + } + + /** + * @param bool $bIsColorComponent + * + * @return Size + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState, $bIsColorComponent = false) + { + $sSize = ''; + if ($oParserState->comes('-')) { + $sSize .= $oParserState->consume('-'); + } + while (is_numeric($oParserState->peek()) || $oParserState->comes('.')) { + if ($oParserState->comes('.')) { + $sSize .= $oParserState->consume('.'); + } else { + $sSize .= $oParserState->consume(1); + } + } + + $sUnit = null; + $aSizeUnits = self::getSizeUnits(); + foreach ($aSizeUnits as $iLength => &$aValues) { + $sKey = strtolower($oParserState->peek($iLength)); + if (array_key_exists($sKey, $aValues)) { + if (($sUnit = $aValues[$sKey]) !== null) { + $oParserState->consume($iLength); + break; + } + } + } + return new Size((float)$sSize, $sUnit, $bIsColorComponent, $oParserState->currentLine()); + } + + /** + * @return array> + */ + private static function getSizeUnits() + { + if (!is_array(self::$SIZE_UNITS)) { + self::$SIZE_UNITS = []; + foreach (array_merge(self::ABSOLUTE_SIZE_UNITS, self::RELATIVE_SIZE_UNITS, self::NON_SIZE_UNITS) as $val) { + $iSize = strlen($val); + if (!isset(self::$SIZE_UNITS[$iSize])) { + self::$SIZE_UNITS[$iSize] = []; + } + self::$SIZE_UNITS[$iSize][strtolower($val)] = $val; + } + + krsort(self::$SIZE_UNITS, SORT_NUMERIC); + } + + return self::$SIZE_UNITS; + } + + /** + * @param string $sUnit + * + * @return void + */ + public function setUnit($sUnit) + { + $this->sUnit = $sUnit; + } + + /** + * @return string|null + */ + public function getUnit() + { + return $this->sUnit; + } + + /** + * @param float|int|string $fSize + */ + public function setSize($fSize) + { + $this->fSize = (float)$fSize; + } + + /** + * @return float + */ + public function getSize() + { + return $this->fSize; + } + + /** + * @return bool + */ + public function isColorComponent() + { + return $this->bIsColorComponent; + } + + /** + * Returns whether the number stored in this Size really represents a size (as in a length of something on screen). + * + * @return false if the unit an angle, a duration, a frequency or the number is a component in a Color object. + */ + public function isSize() + { + if (in_array($this->sUnit, self::NON_SIZE_UNITS, true)) { + return false; + } + return !$this->isColorComponent(); + } + + /** + * @return bool + */ + public function isRelative() + { + if (in_array($this->sUnit, self::RELATIVE_SIZE_UNITS, true)) { + return true; + } + if ($this->sUnit === null && $this->fSize != 0) { + return true; + } + return false; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + $l = localeconv(); + $sPoint = preg_quote($l['decimal_point'], '/'); + $sSize = preg_match("/[\d\.]+e[+-]?\d+/i", (string)$this->fSize) + ? preg_replace("/$sPoint?0+$/", "", sprintf("%f", $this->fSize)) : $this->fSize; + return preg_replace(["/$sPoint/", "/^(-?)0\./"], ['.', '$1.'], $sSize) + . ($this->sUnit === null ? '' : $this->sUnit); + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/URL.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/URL.php new file mode 100644 index 000000000..1467d505c --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/URL.php @@ -0,0 +1,82 @@ +oURL = $oURL; + } + + /** + * @return URL + * + * @throws SourceException + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parse(ParserState $oParserState) + { + $bUseUrl = $oParserState->comes('url', true); + if ($bUseUrl) { + $oParserState->consume('url'); + $oParserState->consumeWhiteSpace(); + $oParserState->consume('('); + } + $oParserState->consumeWhiteSpace(); + $oResult = new URL(CSSString::parse($oParserState), $oParserState->currentLine()); + if ($bUseUrl) { + $oParserState->consumeWhiteSpace(); + $oParserState->consume(')'); + } + return $oResult; + } + + /** + * @return void + */ + public function setURL(CSSString $oURL) + { + $this->oURL = $oURL; + } + + /** + * @return CSSString + */ + public function getURL() + { + return $this->oURL; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return "url({$this->oURL->render($oOutputFormat)})"; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Value.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Value.php new file mode 100644 index 000000000..66cb9fd47 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/Value.php @@ -0,0 +1,198 @@ +iLineNo = $iLineNo; + } + + /** + * @param array $aListDelimiters + * + * @return RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string + * + * @throws UnexpectedTokenException + * @throws UnexpectedEOFException + */ + public static function parseValue(ParserState $oParserState, array $aListDelimiters = []) + { + /** @var array $aStack */ + $aStack = []; + $oParserState->consumeWhiteSpace(); + //Build a list of delimiters and parsed values + while ( + !($oParserState->comes('}') || $oParserState->comes(';') || $oParserState->comes('!') + || $oParserState->comes(')') + || $oParserState->comes('\\')) + ) { + if (count($aStack) > 0) { + $bFoundDelimiter = false; + foreach ($aListDelimiters as $sDelimiter) { + if ($oParserState->comes($sDelimiter)) { + array_push($aStack, $oParserState->consume($sDelimiter)); + $oParserState->consumeWhiteSpace(); + $bFoundDelimiter = true; + break; + } + } + if (!$bFoundDelimiter) { + //Whitespace was the list delimiter + array_push($aStack, ' '); + } + } + array_push($aStack, self::parsePrimitiveValue($oParserState)); + $oParserState->consumeWhiteSpace(); + } + // Convert the list to list objects + foreach ($aListDelimiters as $sDelimiter) { + if (count($aStack) === 1) { + return $aStack[0]; + } + $iStartPosition = null; + while (($iStartPosition = array_search($sDelimiter, $aStack, true)) !== false) { + $iLength = 2; //Number of elements to be joined + for ($i = $iStartPosition + 2; $i < count($aStack); $i += 2, ++$iLength) { + if ($sDelimiter !== $aStack[$i]) { + break; + } + } + $oList = new RuleValueList($sDelimiter, $oParserState->currentLine()); + for ($i = $iStartPosition - 1; $i - $iStartPosition + 1 < $iLength * 2; $i += 2) { + $oList->addListComponent($aStack[$i]); + } + array_splice($aStack, $iStartPosition - 1, $iLength * 2 - 1, [$oList]); + } + } + if (!isset($aStack[0])) { + throw new UnexpectedTokenException( + " {$oParserState->peek()} ", + $oParserState->peek(1, -1) . $oParserState->peek(2), + 'literal', + $oParserState->currentLine() + ); + } + return $aStack[0]; + } + + /** + * @param bool $bIgnoreCase + * + * @return CSSFunction|string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false) + { + $sResult = $oParserState->parseIdentifier($bIgnoreCase); + + if ($oParserState->comes('(')) { + $oParserState->consume('('); + $aArguments = Value::parseValue($oParserState, ['=', ' ', ',']); + $sResult = new CSSFunction($sResult, $aArguments, ',', $oParserState->currentLine()); + $oParserState->consume(')'); + } + + return $sResult; + } + + /** + * @return CSSFunction|CSSString|LineName|Size|URL|string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + * @throws SourceException + */ + public static function parsePrimitiveValue(ParserState $oParserState) + { + $oValue = null; + $oParserState->consumeWhiteSpace(); + if ( + is_numeric($oParserState->peek()) + || ($oParserState->comes('-.') + && is_numeric($oParserState->peek(1, 2))) + || (($oParserState->comes('-') || $oParserState->comes('.')) && is_numeric($oParserState->peek(1, 1))) + ) { + $oValue = Size::parse($oParserState); + } elseif ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) { + $oValue = Color::parse($oParserState); + } elseif ($oParserState->comes('url', true)) { + $oValue = URL::parse($oParserState); + } elseif ( + $oParserState->comes('calc', true) || $oParserState->comes('-webkit-calc', true) + || $oParserState->comes('-moz-calc', true) + ) { + $oValue = CalcFunction::parse($oParserState); + } elseif ($oParserState->comes("'") || $oParserState->comes('"')) { + $oValue = CSSString::parse($oParserState); + } elseif ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) { + $oValue = self::parseMicrosoftFilter($oParserState); + } elseif ($oParserState->comes("[")) { + $oValue = LineName::parse($oParserState); + } elseif ($oParserState->comes("U+")) { + $oValue = self::parseUnicodeRangeValue($oParserState); + } else { + $oValue = self::parseIdentifierOrFunction($oParserState); + } + $oParserState->consumeWhiteSpace(); + return $oValue; + } + + /** + * @return CSSFunction + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + private static function parseMicrosoftFilter(ParserState $oParserState) + { + $sFunction = $oParserState->consumeUntil('(', false, true); + $aArguments = Value::parseValue($oParserState, [',', '=']); + return new CSSFunction($sFunction, $aArguments, ',', $oParserState->currentLine()); + } + + /** + * @return string + * + * @throws UnexpectedEOFException + * @throws UnexpectedTokenException + */ + private static function parseUnicodeRangeValue(ParserState $oParserState) + { + $iCodepointMaxLength = 6; // Code points outside BMP can use up to six digits + $sRange = ""; + $oParserState->consume("U+"); + do { + if ($oParserState->comes('-')) { + $iCodepointMaxLength = 13; // Max length is 2 six digit code points + the dash(-) between them + } + $sRange .= $oParserState->consume(1); + } while (strlen($sRange) < $iCodepointMaxLength && preg_match("/[A-Fa-f0-9\?-]/", $oParserState->peek())); + return "U+{$sRange}"; + } + + /** + * @return int + */ + public function getLineNo() + { + return $this->iLineNo; + } +} diff --git a/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/ValueList.php b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/ValueList.php new file mode 100644 index 000000000..af5348b96 --- /dev/null +++ b/library/vendor/dompdf/vendor/sabberworm/php-css-parser/src/Value/ValueList.php @@ -0,0 +1,100 @@ + + */ + protected $aComponents; + + /** + * @var string + */ + protected $sSeparator; + + /** + * phpcs:ignore Generic.Files.LineLength + * @param array|RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string $aComponents + * @param string $sSeparator + * @param int $iLineNo + */ + public function __construct($aComponents = [], $sSeparator = ',', $iLineNo = 0) + { + parent::__construct($iLineNo); + if (!is_array($aComponents)) { + $aComponents = [$aComponents]; + } + $this->aComponents = $aComponents; + $this->sSeparator = $sSeparator; + } + + /** + * @param RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string $mComponent + * + * @return void + */ + public function addListComponent($mComponent) + { + $this->aComponents[] = $mComponent; + } + + /** + * @return array + */ + public function getListComponents() + { + return $this->aComponents; + } + + /** + * @param array $aComponents + * + * @return void + */ + public function setListComponents(array $aComponents) + { + $this->aComponents = $aComponents; + } + + /** + * @return string + */ + public function getListSeparator() + { + return $this->sSeparator; + } + + /** + * @param string $sSeparator + * + * @return void + */ + public function setListSeparator($sSeparator) + { + $this->sSeparator = $sSeparator; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(new OutputFormat()); + } + + /** + * @return string + */ + public function render(OutputFormat $oOutputFormat) + { + return $oOutputFormat->implode( + $oOutputFormat->spaceBeforeListArgumentSeparator($this->sSeparator) . $this->sSeparator + . $oOutputFormat->spaceAfterListArgumentSeparator($this->sSeparator), + $this->aComponents + ); + } +}