[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.yml]\nindent_size = 2\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "content": "# CS: no whitespace before return type colon\n6937c3bf9f57533b310c28c1758f4af6bd777f92\n# CS: miscellaneous other whitespace fixes\n92a009284c653e8576ee544191f2167f97c93aed\n# Fix style issues\n0339a6b726db84790d94794e36f502c52e0e518a\n# CS fixes\n2e32a6d48972b2c1976ed5d8967145b6cec4a4a9\n# CS fixes\ne64841657859b6a3d1ecf01d6922ce1548ad7382\n# fix code style\n81dea8effddb6afc070552d95b8f5bbc2589fe10\n# Style fixes\n0cbfc1c752b9fae8c7a0be740e5d6af496463e1e\n# Apply doctrine coding standards\nd0e860a40b8199a0d5166023bc3764eed7af7d83\n"
  },
  {
    "path": ".gitattributes",
    "content": "/.github/ export-ignore\n/examples/ export-ignore\n/docs/ export-ignore\n/tests/ export-ignore\n/.editorconfig export-ignore\n/.git-blame-ignore-revs export-ignore\n/.gitattributes export-ignore\n/.gitignore export-ignore\n/.yamllint.yaml export-ignore\n/Makefile export-ignore\n/composer-require-checker.json export-ignore\n/composer.lock export-ignore\n/phpbench.json export-ignore\n/phpcs.xml.dist export-ignore\n/phpstan.neon export-ignore\n/phpunit.xml.dist export-ignore\n/psalm.xml export-ignore\n/phpdoc.dist.xml export-ignore\n/.phpdoc/ export-ignore\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"composer\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n    open-pull-requests-limit: 10\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/documentation.yaml",
    "content": "# https://docs.github.com/en/actions\n\nname: \"Documentation\"\n\non: # yamllint disable-line rule:truthy\n    push:\n        branches:\n            - \"2.x\"\n    workflow_dispatch: null\n\njobs:\n    run:\n        name: \"Documentation\"\n        uses: \"phpDocumentor/.github/.github/workflows/documentation.yml@main\"\n        with:\n            deploy: true\n            component: \"type-resolver\"\n        secrets:\n            token: \"${{ secrets.BOT_TOKEN }}\"\n"
  },
  {
    "path": ".github/workflows/integrate.yaml",
    "content": "# https://docs.github.com/en/actions\n\nname: \"Integrate\"\n\non: # yamllint disable-line rule:truthy\n  push:\n    branches:\n      - \"1.x\"\n  pull_request: null\n  schedule:\n    - cron: \"0 14 * * 1\"\n  # Allow manually triggering the workflow.\n  workflow_dispatch: null\n\njobs:\n  code-coverage:\n    name: \"Code Coverage\"\n    uses: \"phpDocumentor/.github/.github/workflows/code-coverage.yml@main\"\n    with:\n      php-extensions: \"none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, iconv\"\n      composer-root-version: \"1.x-dev\"\n\n  coding-standards:\n    name: \"Coding Standards\"\n    uses: \"phpDocumentor/.github/.github/workflows/coding-standards.yml@v0.9\"\n    with:\n      php-extensions: \"none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, iconv\"\n      composer-root-version: \"1.x-dev\"\n\n  dependency-analysis:\n    name: \"Dependency analysis\"\n    uses: \"phpDocumentor/.github/.github/workflows/dependency-analysis.yml@v0.9\"\n    with:\n      php-extensions: \"none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, iconv\"\n      composer-root-version: \"1.x-dev\"\n\n  lint-root:\n    name: \"Lint root\"\n    uses: \"phpDocumentor/.github/.github/workflows/lint.yml@main\"\n    with:\n      composer-options: \"--no-check-publish --ansi\"\n\n  static-analysis:\n    name: \"Static analysis\"\n    uses: \"phpDocumentor/.github/.github/workflows/static-analysis.yml@v0.9\"\n    with:\n      php-extensions: \"none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, pcntl, posix, iconv\"\n      composer-root-version: \"1.x-dev\"\n\n  unit-tests:\n    name: \"Unit test\"\n    uses: \"phpDocumentor/.github/.github/workflows/continuous-integration.yml@v0.9\"\n    with:\n      php-extensions: \"none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, iconv\"\n      composer-root-version: \"1.x-dev\"\n      upcoming-releases: true\n"
  },
  {
    "path": ".gitignore",
    "content": "# IDE Shizzle; it is recommended to use a global .gitignore for this but since this is an OSS project we want to make\n# it easy to contribute\n.idea\n.vscode\n/nbproject/private/\n.buildpath\n.project\n.settings\n\n# No need to version the binary files of other tools\nbin/behat*\nbin/phpcs*\nbin/phpunit*\nbin/jsonlint*\nbin/validate-json*\ntemp/ecs/*\n\n# Build folder and vendor folder are generated code; no need to version this\nbuild/\ntools/\nvendor/\n*.phar\n\n# By default the phpunit.xml.dist is provided; you can override this using a local config file\nphpunit.xml\n.phpunit.result.cache\n"
  },
  {
    "path": ".phpdoc/template/base.html.twig",
    "content": "{% extends 'layout.html.twig' %}\n\n{% set topMenu = {\n    \"menu\": [\n        { \"name\": \"About\", \"url\": \"https://phpdoc.org/\"},\n        { \"name\": \"Components\", \"url\": \"https://phpdoc.org/components.html\"},\n        { \"name\": \"Documentation\", \"url\": \"https://docs.phpdoc.org/\"},\n    ],\n    \"social\": [\n        { \"iconClass\": \"fab fa-mastodon\", \"url\": \"https://phpc.social/@phpdoc\"},\n        { \"iconClass\": \"fab fa-github\", \"url\": \"https://github.com/phpdocumentor/typeresolver\"},\n        { \"iconClass\": \"fas fa-envelope-open-text\", \"url\": \"https://github.com/orgs/phpDocumentor/discussions\"}\n    ]\n}\n%}\n"
  },
  {
    "path": ".yamllint.yaml",
    "content": "extends: \"default\"\n\nignore: |\n  .build/\n  .notes/\n  vendor/\nrules:\n  braces:\n    max-spaces-inside-empty: 0\n    max-spaces-inside: 1\n    min-spaces-inside-empty: 0\n    min-spaces-inside: 1\n  brackets:\n    max-spaces-inside-empty: 0\n    max-spaces-inside: 0\n    min-spaces-inside-empty: 0\n    min-spaces-inside: 0\n  colons:\n    max-spaces-after: 1\n    max-spaces-before: 0\n  commas:\n    max-spaces-after: 1\n    max-spaces-before: 0\n    min-spaces-after: 1\n  comments:\n    ignore-shebangs: true\n    min-spaces-from-content: 1\n    require-starting-space: true\n  comments-indentation: \"enable\"\n  document-end:\n    present: false\n  document-start:\n    present: false\n  indentation:\n    check-multi-line-strings: false\n    indent-sequences: true\n    spaces: 2\n  empty-lines:\n    max-end: 0\n    max-start: 0\n    max: 1\n  empty-values:\n    forbid-in-block-mappings: true\n    forbid-in-flow-mappings: true\n  hyphens:\n    max-spaces-after: 2\n  key-duplicates: \"enable\"\n  key-ordering: \"disable\"\n  line-length: \"disable\"\n  new-line-at-end-of-file: \"enable\"\n  new-lines:\n    type: \"unix\"\n  octal-values:\n    forbid-implicit-octal: true\n  quoted-strings:\n    quote-type: \"double\"\n  trailing-spaces: \"enable\"\n  truthy:\n    allowed-values:\n      - \"false\"\n      - \"true\"\n\nyaml-files:\n  - \"*.yaml\"\n  - \"*.yml\"\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2010 Mike van Riel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: help\nhelp: ## Displays this list of targets with descriptions\n\t@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = \":.*?## \"}; {printf \"\\033[32m%-30s\\033[0m %s\\n\", $$1, $$2}'\n\n.PHONY: code-style\ncode-style:\n\tdocker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest -d memory_limit=1024M -s\n\n.PHONY: fix-code-style\nfix-code-style:\n\tdocker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest phpcbf\n\n.PHONY: static-code-analysis\nstatic-code-analysis: vendor ## Runs a static code analysis with phpstan/phpstan and vimeo/psalm\n\tdocker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpstan --configuration=phpstan.neon\n\tdocker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/psalm\n\n.PHONY: test\ntest: test-unit ## Runs all test suites with phpunit/phpunit\n\tdocker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit\n\n.PHONY: test-unit\ntest-unit: ## Runs unit tests with phpunit/phpunit\n\tdocker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit --testsuite=unit\n\n.PHONY: dependency-analysis\ndependency-analysis: vendor ## Runs a dependency analysis with maglnet/composer-require-checker\n\tdocker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 .phive/composer-require-checker check --config-file=/opt/project/composer-require-checker.json\n\nvendor: composer.json composer.lock\n\tcomposer validate --no-check-publish\n\tcomposer install --no-interaction --no-progress\n\n.PHONY: benchmark\nbenchmark:\n\tdocker run -it --rm -v${CURDIR}:/opt/project -w /opt/project php:7.4-cli vendor/bin/phpbench run\n\n.PHONY: pre-commit-test\npre-commit-test: fix-code-style test code-style static-code-analysis\n\n.PHONY: docs\ndocs: ## Generate documentation with phpDocumentor\n\tdocker run -it --rm -v${CURDIR}:/opt/project -w /opt/project phpdoc/phpdoc:3\n"
  },
  {
    "path": "README.md",
    "content": "[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n![](https://github.com/phpdocumentor/typeresolver/workflows/Qa%20workflow/badge.svg?branch=1.x)\n[![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/TypeResolver.svg)](https://coveralls.io/github/phpDocumentor/TypeResolver?branch=1.x)\n[![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/TypeResolver.svg)](https://scrutinizer-ci.com/g/phpDocumentor/TypeResolver/?branch=1.x)\n[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/TypeResolver.svg)](https://scrutinizer-ci.com/g/phpDocumentor/TypeResolver/?branch=1.x)\n![Packagist Version](https://img.shields.io/packagist/v/phpdocumentor/type-resolver?label=Packagist%20stable)\n![Packagist Version](https://img.shields.io/packagist/vpre/phpdocumentor/type-resolver?label=Packagist%20unstable)\n\nTypeResolver and FqsenResolver\n==============================\n\nThe specification on types in DocBlocks (PSR-5) describes various keywords and special constructs\nbut also how to statically resolve the partial name of a Class into a Fully Qualified Class Name (FQCN).\n\nPSR-5 also introduces an additional way to describe deeper elements than Classes, Interfaces and Traits \ncalled the Fully Qualified Structural Element Name (FQSEN). Using this it is possible to refer to methods,\nproperties and class constants but also functions and global constants.\n\nThis package provides two Resolvers that are capable of \n\n1. Returning a series of Value Object for given expression while resolving any partial class names, and \n2. Returning an FQSEN object after resolving any partial Structural Element Names into Fully Qualified Structural \n   Element names.\n\n## Installing\n\nThe easiest way to install this library is with [Composer](https://getcomposer.org) using the following command:\n\n    $ composer require phpdocumentor/type-resolver\n\n## Examples\n\nReady to dive in and don't want to read through all that text below? Just consult the [examples](examples) folder and check which type of action that your want to accomplish.\n\n## On Types and Element Names\n\nThis component can be used in one of two ways\n \n1. To resolve a Type or\n2. To resolve a Fully Qualified Structural Element Name\n \nThe big difference between these two is in the number of things it can resolve. \n\nThe TypeResolver can resolve:\n\n- a php primitive or pseudo-primitive such as a string or void (`@var string` or `@return void`).\n- a composite such as an array of string (`@var string[]`).\n- a compound such as a string or integer (`@var string|integer`).\n- an array expression (`@var (string|TypeResolver)[]`)\n- an object or interface such as the TypeResolver class (`@var TypeResolver` \n  or `@var \\phpDocumentor\\Reflection\\TypeResolver`)\n\n  > please note that if you want to pass partial class names that additional steps are necessary, see the \n  > chapter `Resolving partial classes and FQSENs` for more information.\n\nWhere the FqsenResolver can resolve:\n\n- Constant expressions (i.e. `@see \\MyNamespace\\MY_CONSTANT`)\n- Function expressions (i.e. `@see \\MyNamespace\\myFunction()`)\n- Class expressions (i.e. `@see \\MyNamespace\\MyClass`)\n- Interface expressions (i.e. `@see \\MyNamespace\\MyInterface`)\n- Trait expressions (i.e. `@see \\MyNamespace\\MyTrait`)\n- Class constant expressions (i.e. `@see \\MyNamespace\\MyClass::MY_CONSTANT`)\n- Property expressions (i.e. `@see \\MyNamespace\\MyClass::$myProperty`)\n- Method expressions (i.e. `@see \\MyNamespace\\MyClass::myMethod()`)\n\n## Resolving a type\n\nIn order to resolve a type you will have to instantiate the class `\\phpDocumentor\\Reflection\\TypeResolver` and call its `resolve` method like this:\n\n```php\n$typeResolver = new \\phpDocumentor\\Reflection\\TypeResolver();\n$type = $typeResolver->resolve('string|integer');\n```\n\nIn this example you will receive a Value Object of class `\\phpDocumentor\\Reflection\\Types\\Compound` that has two \nelements, one of type `\\phpDocumentor\\Reflection\\Types\\String_` and one of type \n`\\phpDocumentor\\Reflection\\Types\\Integer`.\n\nThe real power of this resolver is in its capability to expand partial class names into fully qualified class names; but in order to do that we need an additional `\\phpDocumentor\\Reflection\\Types\\Context` class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.\n\n### Resolving nullable types\n\nPhp 7.1 introduced nullable types e.g. `?string`. Type resolver will resolve the original type without the nullable notation `?`\njust like it would do without the `?`. After that the type is wrapped in a `\\phpDocumentor\\Reflection\\Types\\Nullable` object.\nThe `Nullable` type has a method to fetch the actual type. \n\n## Resolving an FQSEN\n\nA Fully Qualified Structural Element Name is a reference to another element in your code bases and can be resolved using the `\\phpDocumentor\\Reflection\\FqsenResolver` class' `resolve` method, like this:\n\n```php\n$fqsenResolver = new \\phpDocumentor\\Reflection\\FqsenResolver();\n$fqsen = $fqsenResolver->resolve('\\phpDocumentor\\Reflection\\FqsenResolver::resolve()');\n```\n\nIn this example we resolve a Fully Qualified Structural Element Name (meaning that it includes the full namespace, class name and element name) and receive a Value Object of type `\\phpDocumentor\\Reflection\\Fqsen`.\n\nThe real power of this resolver is in its capability to expand partial element names into Fully Qualified Structural Element Names; but in order to do that we need an additional `\\phpDocumentor\\Reflection\\Types\\Context` class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.\n\n## Resolving partial Classes and Structural Element Names\n\nPerhaps the best feature of this library is that it knows how to resolve partial class names into fully qualified class names.\n\nFor example, you have this file:\n\n```php\nnamespace My\\Example;\n\nuse phpDocumentor\\Reflection\\Types;\n\nclass Classy\n{\n    /**\n     * @var Types\\Context\n     * @see Classy::otherFunction()\n     */\n    public function __construct($context) {}\n    \n    public function otherFunction(){}\n}\n```\n\nSuppose that you would want to resolve (and expand) the type in the `@var` tag and the element name in the `@see` tag.\n\nFor the resolvers to know how to expand partial names you have to provide a bit of _Context_ for them by instantiating a new class named `\\phpDocumentor\\Reflection\\Types\\Context` with the name of the namespace and the aliases that are in play.\n\n### Creating a Context\n\nYou can do this by manually creating a Context like this:\n\n```php\n$context = new \\phpDocumentor\\Reflection\\Types\\Context(\n    '\\My\\Example', \n    [ 'Types' => '\\phpDocumentor\\Reflection\\Types']\n);\n```\n\nOr by using the `\\phpDocumentor\\Reflection\\Types\\ContextFactory` to instantiate a new context based on a Reflector object or by providing the namespace that you'd like to extract and the source code of the file in which the given type expression occurs.\n\n```php\n$contextFactory = new \\phpDocumentor\\Reflection\\Types\\ContextFactory();\n$context = $contextFactory->createFromReflector(new ReflectionMethod('\\My\\Example\\Classy', '__construct'));\n```\n\nor\n\n```php\n$contextFactory = new \\phpDocumentor\\Reflection\\Types\\ContextFactory();\n$context = $contextFactory->createForNamespace('\\My\\Example', file_get_contents('My/Example/Classy.php'));\n```\n\n### Using the Context\n\nAfter you have obtained a Context it is just a matter of passing it along with the `resolve` method of either Resolver class as second argument and the Resolvers will take this into account when resolving partial names.\n\nTo obtain the resolved class name for the `@var` tag in the example above you can do:\n\n```php\n$typeResolver = new \\phpDocumentor\\Reflection\\TypeResolver();\n$type = $typeResolver->resolve('Types\\Context', $context);\n```\n\nWhen you do this you will receive an object of class `\\phpDocumentor\\Reflection\\Types\\Object_` for which you can call the `getFqsen` method to receive a Value Object that represents the complete FQSEN. So that would be `phpDocumentor\\Reflection\\Types\\Context`.\n\n> Why is the FQSEN wrapped in another object `Object_`?\n> \n> The resolve method of the TypeResolver only returns object with the interface `Type` and the FQSEN is a common type that does not represent a Type. Also: in some cases a type can represent an \"Untyped Object\", meaning that it is an object (signified by the `object` keyword) but does not refer to a specific element using an FQSEN.\n\nAnother example is on how to resolve the FQSEN of a method as can be seen with the `@see` tag in the example above. To resolve that you can do the following:\n\n```php\n$fqsenResolver = new \\phpDocumentor\\Reflection\\FqsenResolver();\n$type = $fqsenResolver->resolve('Classy::otherFunction()', $context);\n```\n\nBecause Classy is a Class in the current namespace its FQSEN will have the `My\\Example` namespace and by calling the `resolve` method of the FQSEN Resolver you will receive an `Fqsen` object that refers to `\\My\\Example\\Classy::otherFunction()`.\n"
  },
  {
    "path": "composer-require-checker.json",
    "content": "{\n  \"symbol-whitelist\" : [\n    \"null\", \"true\", \"false\",\n    \"static\", \"self\", \"parent\",\n    \"array\", \"string\", \"int\", \"float\", \"bool\", \"iterable\", \"callable\", \"void\", \"object\", \"XSLTProcessor\", \"PHPStan\\\\PhpDocParser\\\\ParserConfig\",\n    \"T_NAME_QUALIFIED\", \"T_NAME_FULLY_QUALIFIED\"\n  ],\n  \"php-core-extensions\" : [\n    \"Core\",\n    \"pcre\",\n    \"Reflection\",\n    \"tokenizer\",\n    \"SPL\",\n    \"standard\"\n  ]\n}\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"phpdocumentor/type-resolver\",\n    \"description\": \"A PSR-5 based resolver of Class names, Types and Structural Element Names\",\n    \"type\": \"library\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n          \"name\": \"Mike van Riel\",\n          \"email\": \"me@mikevanriel.com\"\n        }\n    ],\n    \"require\": {\n        \"php\": \"^7.4 || ^8.0\",\n        \"phpdocumentor/reflection-common\": \"^2.0\",\n        \"phpstan/phpdoc-parser\": \"^2.0\",\n        \"doctrine/deprecations\": \"^1.0\"\n    },\n    \"require-dev\": {\n        \"ext-tokenizer\": \"*\",\n        \"phpunit/phpunit\": \"^9.5\",\n        \"phpstan/phpstan\": \"^2.1\",\n        \"phpstan/phpstan-phpunit\": \"^2.0\",\n        \"phpstan/extension-installer\": \"^1.4\",\n        \"phpbench/phpbench\": \"^1.2\",\n        \"psalm/phar\": \"^4\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"phpDocumentor\\\\Reflection\\\\\": \"src\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"phpDocumentor\\\\Reflection\\\\\": [\"tests/unit\", \"tests/benchmark\"]\n        }\n    },\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-1.x\": \"1.x-dev\",\n            \"dev-2.x\": \"2.x-dev\"\n        }\n    },\n    \"config\": {\n        \"platform\": {\n            \"php\": \"7.4.0\"\n        },\n        \"allow-plugins\": {\n            \"phpstan/extension-installer\": true\n        }\n    }\n}\n"
  },
  {
    "path": "docs/generics.rst",
    "content": "========\nGenerics\n========\n\nThis project is capable of parsing generics notation as used by PHPStan. But it has some limitations, in regards to\nPHPStan. The main difference is that PHPStan does scan your whole codebase to find out what types are used in generics,\nwhile this library only parses the types as they are given to it.\n\nThis means that if you use a generic type like.\n\n.. code:: php\n\n    namespace MyApp;\n\n    /**\n     * @template T of Item\n     */\n    class Collection {\n\n        /**\n         * @return T[]\n         */\n        public function getItems() : array {\n            // ...\n        }\n    }\n\nThe type resolver will not be able to determine what ``T`` is. In fact there is no difference between ``T`` and any other relative\nused classname like ``Item``. The resolver will handle ``T`` as a normal class name. In this example it will resolve ``T`` to ``\\MyApp\\T``.\n"
  },
  {
    "path": "docs/getting-started.rst",
    "content": "===============\nGetting started\n===============\n\nOn this page you will find a brief introduction on how to use the TypeResolver in your project.\n\nInstallation\n============\n\nThe TypeResolver is available on Packagist and can be installed using Composer:\n\n.. code:: bash\n    composer require phpdocumentor/type-resolver\n\n\nGeneral usage\n===========\n\nAfter you installed the TypeResolver you can use it in your project. This can be done by creating a new instance\nof the :php:class:`\\phpDocumentor\\Reflection\\TypeResolver` class and calling\n:php:method:`\\phpDocumentor\\Reflection\\TypeResolver::resolve()` with the type you want to resolve.\n\n.. code:: php\n    $typeResolver = new \\phpDocumentor\\Reflection\\TypeResolver();\n    $type = $typeResolver->resolve('string');\n    echo get_class($type); // phpDocumentor\\Reflection\\Types\\String_\n\nThe real power of this resolver is in its capability to expand partial class names into fully qualified class names;\nbut in order to do that we need an additional :php:class:`\\phpDocumentor\\Reflection\\Types\\Context` class that\nwill inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.\n\nRead more about the Context class in the next section.\n"
  },
  {
    "path": "docs/index.rst",
    "content": "=============\nType resolver\n=============\n\nThis project part of the phpDocumentor project. It is capable of creating an object structure of the type\nspecifications found in the PHPDoc blocks of a project. This can be useful for static analysis of a project\nor other behavior that requires knowledge of the types used in a project like automatically build forms.\n\nThis project aims to cover all types that are available in PHPDoc and PHP itself. And is open for extension by\nthird party developers.\n\n.. toctree::\n   :maxdepth: 2\n   :hidden:\n\n   index\n   getting-started\n   generics\n   upgrade-v1-to-v2\n"
  },
  {
    "path": "docs/upgrade-v1-to-v2.rst",
    "content": "====================\nUpgrade to Version 2\n====================\n\nVersion 2 of the Type Resolver introduces several breaking changes and new features. This guide will help you\nupgrade your codebase to be compatible with the latest version. The usage of the TypeResolver remains the same. However,\nsome classes have been moved or replaced, and the minimum PHP version requirement has been raised.\n\nPHP Version\n-----------\n\nVersion 2 requires PHP 7.4 or higher. We have been supporting PHP 7.3 in version 1, but due to changing constraints\nin our dependencies, we have had to raise the minimum PHP version. At the moment of writing this, PHP 7.3 is used by 2%\nof all installations of this package according to Packagist. We believe this is a reasonable trade-off to ensure we\ncan continue to deliver new features and improvements.\n\nMoved classes\n-------------\n\n- ``phpDocumentor\\Reflection\\Types\\InterfaceString`` => :php:class:`phpDocumentor\\Reflection\\PseudoTypes\\InterfaceString`\n- ``phpDocumentor\\Reflection\\Types\\ClassString`` => :php:class:`phpDocumentor\\Reflection\\PseudoTypes\\ClassString`\n- ``phpDocumentor\\Reflection\\Types\\ArrayKey`` => :php:class:`phpDocumentor\\Reflection\\PseudoTypes\\ArrayKey`\n- ``phpDocumentor\\Reflection\\Types\\True_`` => :php:class:`phpDocumentor\\Reflection\\PseudoTypes\\True_`\n- ``phpDocumentor\\Reflection\\Types\\False_`` => :php:class:`phpDocumentor\\Reflection\\PseudoTypes\\False_`\n\nReplaced classes\n-----------------\n\n- ``phpDocumentor\\Reflection\\Types\\Collection`` => :php:class:`phpDocumentor\\Reflection\\PseudoTypes\\Generic`\n\nSince the introduction of generics in PHP this library was not capable of parsing them correctly. The old Collection\nwas blocking the use of generics. The new Generic type is a representation of generics like supported in the eco system.\n\nChanged implementations\n-----------------------\n\n:php:class:`phpDocumentor\\Reflection\\PseudoTypes\\InterfaceString`, :php:class:`phpDocumentor\\Reflection\\PseudoTypes\\ClassString` and\n:php:class:`phpDocumentor\\Reflection\\PseudoTypes\\TraitString` are no longer returning a :php:class:`phpDocumentor\\Reflection\\Fqsen` since\nsupport for generics these classes can have type arguments like any other generic.\n\n"
  },
  {
    "path": "examples/01-resolving-simple-types.php",
    "content": "<?php\n\nuse phpDocumentor\\Reflection\\TypeResolver;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n$typeResolver = new TypeResolver();\n\n// Will yield an object of type phpDocumentor\\Types\\Compound\nvar_export($typeResolver->resolve('string|integer'));\n\n// Will return the string \"string|int\"\nvar_dump((string)$typeResolver->resolve('string|integer'));\n"
  },
  {
    "path": "examples/02-resolving-classes.php",
    "content": "<?php\n\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse phpDocumentor\\Reflection\\TypeResolver;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n$typeResolver = new TypeResolver();\n\n// Will use the namespace and aliases to resolve to \\phpDocumentor\\Types\\Resolver|Mockery\\MockInterface\n$context = new Context('\\phpDocumentor', [ 'm' => 'Mockery' ]);\nvar_dump((string)$typeResolver->resolve('Types\\Resolver|m\\MockInterface', $context));\n"
  },
  {
    "path": "examples/03-resolving-all-elements.php",
    "content": "<?php\n\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse phpDocumentor\\Reflection\\FqsenResolver;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n$fqsenResolver = new FqsenResolver();\n\n// Will use the namespace and aliases to resolve to a Fqsen object\n$context = new Context('\\phpDocumentor\\Types');\n\n// Method named: \\phpDocumentor\\Types\\Types\\Resolver::resolveFqsen()\nvar_dump((string)$fqsenResolver->resolve('Types\\Resolver::resolveFqsen()', $context));\n\n// Property named: \\phpDocumentor\\Types\\Types\\Resolver::$keyWords\nvar_dump((string)$fqsenResolver->resolve('Types\\Resolver::$keyWords', $context));\n"
  },
  {
    "path": "examples/04-discovering-the-context-using-class-reflection.php",
    "content": "<?php\n\nuse phpDocumentor\\Reflection\\FqsenResolver;\nuse phpDocumentor\\Reflection\\TypeResolver;\nuse phpDocumentor\\Reflection\\Types\\ContextFactory;\n\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire 'Classy.php';\n\n$typeResolver = new TypeResolver();\n$fqsenResolver = new FqsenResolver();\n\n$contextFactory = new ContextFactory();\n$context = $contextFactory->createFromReflector(new ReflectionClass('My\\\\Example\\\\Classy'));\n\n// Class named: \\phpDocumentor\\Reflection\\Types\\Resolver\nvar_dump((string)$typeResolver->resolve('Types\\Resolver', $context));\n\n// String\nvar_dump((string)$typeResolver->resolve('string', $context));\n\n// Property named: \\phpDocumentor\\Reflection\\Types\\Resolver::$keyWords\nvar_dump((string)$fqsenResolver->resolve('Types\\Resolver::$keyWords', $context));\n\n// Class named: \\My\\Example\\string\n// - Shows the difference between the FqsenResolver and TypeResolver; the FqsenResolver will assume\n//   that the given value is not a type but most definitely a reference to another element. This is\n//   because conflicts between type keywords and class names can exist and if you know a reference\n//   is not a type but an element you can force that keywords are resolved.\nvar_dump((string)$fqsenResolver->resolve('string', $context));\n"
  },
  {
    "path": "examples/05-discovering-the-context-using-method-reflection.php",
    "content": "<?php\n\nuse phpDocumentor\\Reflection\\FqsenResolver;\nuse phpDocumentor\\Reflection\\TypeResolver;\nuse phpDocumentor\\Reflection\\Types\\ContextFactory;\n\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire 'Classy.php';\n\n$typeResolver = new TypeResolver();\n$fqsenResolver = new FqsenResolver();\n\n$contextFactory = new ContextFactory();\n$context = $contextFactory->createFromReflector(new ReflectionMethod('My\\\\Example\\\\Classy', '__construct'));\n\n// Class named: \\phpDocumentor\\Reflection\\Types\\Resolver\nvar_dump((string)$typeResolver->resolve('Types\\Resolver', $context));\n\n// String\nvar_dump((string)$typeResolver->resolve('string', $context));\n\n// Property named: \\phpDocumentor\\Reflection\\Types\\Resolver::$keyWords\nvar_dump((string)$fqsenResolver->resolve('Types\\Resolver::$keyWords', $context));\n\n// Class named: \\My\\Example\\string\n// - Shows the difference between the FqsenResolver and TypeResolver; the FqsenResolver will assume\n//   that the given value is not a type but most definitely a reference to another element. This is\n//   because conflicts between type keywords and class names can exist and if you know a reference\n//   is not a type but an element you can force that keywords are resolved.\nvar_dump((string)$fqsenResolver->resolve('string', $context));\n"
  },
  {
    "path": "examples/06-discovering-the-context-using-file-contents.php",
    "content": "<?php\n\nuse phpDocumentor\\Reflection\\FqsenResolver;\nuse phpDocumentor\\Reflection\\TypeResolver;\nuse phpDocumentor\\Reflection\\Types\\ContextFactory;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n$typeResolver = new TypeResolver();\n$fqsenResolver = new FqsenResolver();\n\n$contextFactory = new ContextFactory();\n$context = $contextFactory->createForNamespace('My\\Example', file_get_contents(__DIR__ . '/Classy.php'));\n\n// Class named: \\phpDocumentor\\Reflection\\Types\\Resolver\nvar_dump((string)$typeResolver->resolve('Types\\Resolver', $context));\n\n// String\nvar_dump((string)$typeResolver->resolve('string', $context));\n\n// Property named: \\phpDocumentor\\Reflection\\Types\\Resolver::$keyWords\nvar_dump((string)$fqsenResolver->resolve('Types\\Resolver::$keyWords', $context));\n"
  },
  {
    "path": "examples/Classy.php",
    "content": "<?php\n\nnamespace My\\Example;\n\nuse Mockery as m;\nuse phpDocumentor\\Reflection\\Types;\n\nclass Classy\n{\n    /**\n     * @var Types\\Context\n     */\n    public function __construct($context)\n    {\n    }\n}\n"
  },
  {
    "path": "phpbench.json",
    "content": "{\n  \"$schema\":\"./vendor/phpbench/phpbench/phpbench.schema.json\",\n  \"runner.bootstrap\": \"vendor/autoload.php\",\n  \"runner.path\": \"tests/benchmark\",\n  \"runner.file_pattern\": \"*Bench.php\"\n}\n"
  },
  {
    "path": "phpcs.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<ruleset name=\"TypeResolver\">\n    <description>The coding standard for this library.</description>\n\n    <file>src</file>\n    <file>tests/unit</file>\n    <exclude-pattern>*/tests/unit/Types/ContextFactoryTest\\.php</exclude-pattern>\n    <arg value=\"p\"/>\n\n    <!-- Set the minimum PHP version for PHPCompatibility.\n         This should be kept in sync with the requirements in the composer.json file. -->\n    <config name=\"testVersion\" value=\"7.4-\"/>\n\n    <rule ref=\"phpDocumentor\">\n        <!-- Property type declarations are a PHP 7.4 feature. -->\n        <exclude name=\"SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint\"/>\n    </rule>\n\n    <rule ref=\"SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix\">\n        <exclude-pattern>*/src/*/Abstract*\\.php</exclude-pattern>\n    </rule>\n\n    <rule ref=\"PSR1.Files.SideEffects.FoundWithSymbols\">\n        <exclude-pattern>*/src/PseudoTypes/False_\\.php</exclude-pattern>\n        <exclude-pattern>*/src/PseudoTypes/True_\\.php</exclude-pattern>\n    </rule>\n\n\t<!-- Ignore two PHP 8.0 constants which are being polyfilled in the file using them. -->\n    <rule ref=\"PHPCompatibility.Constants.NewConstants.t_name_qualifiedFound\">\n        <exclude-pattern>*/src/Types/ContextFactory\\.php</exclude-pattern>\n    </rule>\n    <rule ref=\"PHPCompatibility.Constants.NewConstants.t_name_fully_qualifiedFound\">\n        <exclude-pattern>*/src/Types/ContextFactory\\.php</exclude-pattern>\n    </rule>\n</ruleset>\n"
  },
  {
    "path": "phpdoc.dist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<phpdocumentor\n        configVersion=\"3\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xmlns=\"https://www.phpdoc.org\"\n        xsi:noNamespaceSchemaLocation=\"data/xsd/phpdoc.xsd\"\n>\n    <title>Type Resolver</title>\n    <paths>\n        <output>build/docs</output>\n    </paths>\n    <version number=\"0.2.0\">\n        <folder>latest</folder>\n        <api>\n            <source dsn=\"./\">\n                <path>src/</path>\n            </source>\n            <output>api</output>\n            <ignore hidden=\"true\" symlinks=\"true\">\n                <path>tests/**/*</path>\n                <path>build/**/*</path>\n                <path>var/**/*</path>\n                <path>vendor/**/*</path>\n            </ignore>\n            <extensions>\n                <extension>php</extension>\n            </extensions>\n            <ignore-tags>\n                <ignore-tag>template</ignore-tag>\n                <ignore-tag>template-extends</ignore-tag>\n                <ignore-tag>template-implements</ignore-tag>\n                <ignore-tag>extends</ignore-tag>\n                <ignore-tag>implements</ignore-tag>\n            </ignore-tags>\n            <default-package-name>phpDocumentor</default-package-name>\n        </api>\n        <guide>\n            <source dsn=\".\">\n                <path>docs</path>\n            </source>\n            <output>guides</output>\n        </guide>\n    </version>\n    <setting name=\"guides.enabled\" value=\"true\"/>\n    <template name=\"default\" />\n</phpdocumentor>\n"
  },
  {
    "path": "phpstan.neon",
    "content": "parameters:\n    bootstrapFiles:\n        - src/PseudoTypes/False_.php\n        - src/PseudoTypes/True_.php\n    level: max\n    excludePaths:\n        - tests/benchmark/Assets/*\n    paths:\n        - src\n        - tests\n    ignoreErrors:\n        # We are intentionally using non-existing classes here\n\n        -\n            message: \"#^Class phpDocumentor\\\\\\\\Reflection\\\\\\\\DocBlock not found#\"\n            count: 1\n            path: tests/unit/Types/ContextFactoryTest.php\n        -\n            message: \"#^Class phpDocumentor\\\\\\\\Reflection\\\\\\\\DocBlock\\\\\\\\Tag not found#\"\n            count: 1\n            path: tests/unit/Types/ContextFactoryTest.php\n\n        # We are intentionally adding invalid parameters here\n        -\n            message: \"#^Parameter \\\\#2 \\\\$typeClassName of method phpDocumentor\\\\\\\\Reflection\\\\\\\\TypeResolver\\\\:\\\\:addKeyword\\\\(\\\\) expects class\\\\-string\\\\<phpDocumentor\\\\\\\\Reflection\\\\\\\\Type\\\\>\\\\, string given\\\\.$#\"\n            count: 2\n            path: tests/unit/TypeResolverTest.php\n\n        # We are intentionally adding invalid parameters here\n        -\n            message: \"#^Parameter \\\\#1 \\\\$types of class phpDocumentor\\\\\\\\Reflection\\\\\\\\Types\\\\\\\\Compound constructor expects array\\\\<phpDocumentor\\\\\\\\Reflection\\\\\\\\Type\\\\>\\\\, array\\\\<int\\\\, string\\\\> given\\\\.$#\"\n            count: 1\n            path: tests/unit/Types/CompoundTest.php\n\n        -\n            message: \"#^Parameter \\\\#1 \\\\$objectOrClass of class ReflectionClass constructor expects class\\\\-string\\\\<Foo\\\\\\\\Bar\\\\>\\\\|Foo\\\\\\\\Bar\\\\, string given\\\\.$#\"\n            count: 1\n            path: tests/unit/Types/ContextFactoryTest.php\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<phpunit\n    bootstrap=\"vendor/autoload.php\"\n    colors=\"true\"\n    verbose=\"true\"\n    convertDeprecationsToExceptions=\"false\"\n>\n    <coverage>\n        <include>\n            <directory suffix=\".php\">src</directory>\n        </include>\n        <report>\n            <clover outputFile=\"build/logs/clover.xml\" />\n            <html outputDirectory=\"build/coverage\" lowUpperBound=\"35\" highLowerBound=\"70\" />\n        </report>\n    </coverage>\n    <testsuites>\n        <testsuite name=\"unit\">\n            <directory>./tests/unit/</directory>\n        </testsuite>\n    </testsuites>\n    <logging>\n        <junit outputFile=\"build/logs/junit.xml\" />\n    </logging>\n</phpunit>\n"
  },
  {
    "path": "psalm.xml",
    "content": "<?xml version=\"1.0\"?>\n<psalm\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns=\"https://getpsalm.org/schema/config\"\n    xsi:schemaLocation=\"https://getpsalm.org/schema/config file:///composer/vendor/vimeo/psalm/config.xsd\"\n    errorLevel=\"1\"\n>\n    <projectFiles>\n        <directory name=\"src\" />\n        <ignoreFiles>\n            <directory name=\"vendor\" />\n        </ignoreFiles>\n    </projectFiles>\n\n    <issueHandlers>\n        <UnnecessaryVarAnnotation>\n            <errorLevel type=\"info\">\n                <file name=\"src/Types/ContextFactory.php\"/>\n            </errorLevel>\n        </UnnecessaryVarAnnotation>\n        <UnusedMethodCall>\n            <errorLevel type=\"suppress\">\n                <referencedMethod name=\"phpDocumentor\\Reflection\\Types\\AggregatedType::add\"/>\n            </errorLevel>\n        </UnusedMethodCall>\n        <DocblockTypeContradiction>\n            <errorLevel type=\"info\">\n                <!-- ArrayIterator::current can return null if iterated even if ArrayIterator::valid isn't checked before -->\n                <file name=\"src/TypeResolver.php\"/>\n                <!-- Not sure what's going on. I don't think it's possible to have false here -->\n                <file name=\"src/Types/ContextFactory.php\"/>\n            </errorLevel>\n        </DocblockTypeContradiction>\n        \n        <RedundantConditionGivenDocblockType>\n            <errorLevel type=\"info\">\n                <!-- ArrayIterator::current can return null if iterated even if ArrayIterator::valid isn't checked before -->\n                <file name=\"src/TypeResolver.php\"/>\n            </errorLevel>\n        </RedundantConditionGivenDocblockType>\n\n        <InvalidArgument>\n            <errorLevel type=\"suppress\">\n                <referencedFunction name=\"PHPStan\\PhpDocParser\\Lexer\\Lexer::__construct\"/>\n                <referencedFunction name=\"PHPStan\\PhpDocParser\\Parser\\ConstExprParser::__construct\"/>\n                <referencedFunction name=\"PHPStan\\PhpDocParser\\Parser\\TypeParser::__construct\"/>\n            </errorLevel>\n        </InvalidArgument>\n\n        <UndefinedDocblockClass>\n            <errorLevel type=\"suppress\">\n                <referencedClass name=\"PHPStan\\PhpDocParser\\ParserConfig\"/>\n            </errorLevel>\n        </UndefinedDocblockClass>\n\n        <MixedArgument>\n            <errorLevel type=\"suppress\">\n                <referencedFunction name=\"PHPStan\\PhpDocParser\\Lexer\\Lexer::__construct\"/>\n                <referencedFunction name=\"PHPStan\\PhpDocParser\\Parser\\ConstExprParser::__construct\"/>\n                <referencedFunction name=\"PHPStan\\PhpDocParser\\Parser\\TypeParser::__construct\"/>\n            </errorLevel>\n        </MixedArgument>\n    </issueHandlers>\n</psalm>\n"
  },
  {
    "path": "src/FqsenResolver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\nuse InvalidArgumentException;\nuse phpDocumentor\\Reflection\\Types\\Context;\n\nuse function explode;\nuse function implode;\nuse function strpos;\n\n/**\n * Resolver for Fqsen using Context information\n *\n * @psalm-immutable\n */\nclass FqsenResolver\n{\n    /** @var string Definition of the NAMESPACE operator in PHP */\n    private const OPERATOR_NAMESPACE = '\\\\';\n\n    public function resolve(string $fqsen, ?Context $context = null): Fqsen\n    {\n        if ($context === null) {\n            $context = new Context('');\n        }\n\n        if ($this->isFqsen($fqsen)) {\n            return new Fqsen($fqsen);\n        }\n\n        return $this->resolvePartialStructuralElementName($fqsen, $context);\n    }\n\n    /**\n     * Tests whether the given type is a Fully Qualified Structural Element Name.\n     */\n    private function isFqsen(string $type): bool\n    {\n        return strpos($type, self::OPERATOR_NAMESPACE) === 0;\n    }\n\n    /**\n     * Resolves a partial Structural Element Name (i.e. `Reflection\\DocBlock`) to its FQSEN representation\n     * (i.e. `\\phpDocumentor\\Reflection\\DocBlock`) based on the Namespace and aliases mentioned in the Context.\n     *\n     * @throws InvalidArgumentException When type is not a valid FQSEN.\n     */\n    private function resolvePartialStructuralElementName(string $type, Context $context): Fqsen\n    {\n        $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2);\n\n        $namespaceAliases = $context->getNamespaceAliases();\n\n        // if the first segment is not an alias; prepend namespace name and return\n        if (!isset($namespaceAliases[$typeParts[0]])) {\n            $namespace = $context->getNamespace();\n            if ($namespace !== '') {\n                $namespace .= self::OPERATOR_NAMESPACE;\n            }\n\n            return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type);\n        }\n\n        $typeParts[0] = $namespaceAliases[$typeParts[0]];\n\n        return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts));\n    }\n}\n"
  },
  {
    "path": "src/PseudoType.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\ninterface PseudoType extends Type\n{\n    public function underlyingType(): Type;\n}\n"
  },
  {
    "path": "src/PseudoTypes/ArrayKey.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\AggregatedType;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type `array-key`.\n *\n * @psalm-immutable\n */\nclass ArrayKey extends AggregatedType implements PseudoType\n{\n    public function __construct()\n    {\n        parent::__construct([new String_(), new Integer()], '|');\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Compound([new String_(), new Integer()]);\n    }\n\n    public function __toString(): string\n    {\n        return 'array-key';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ArrayShape.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\n\nuse function implode;\n\n/** @psalm-immutable */\nclass ArrayShape extends Array_ implements PseudoType\n{\n    /** @var ArrayShapeItem[] */\n    private $items;\n\n    public function __construct(ArrayShapeItem ...$items)\n    {\n        parent::__construct(new Mixed_(), new ArrayKey());\n\n        $this->items = $items;\n    }\n\n    /**\n     * @return ArrayShapeItem[]\n     */\n    public function getItems(): array\n    {\n        return $this->items;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Array_(new Mixed_(), new ArrayKey());\n    }\n\n    public function __toString(): string\n    {\n        return 'array{' . implode(', ', $this->items) . '}';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ArrayShapeItem.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nclass ArrayShapeItem extends ShapeItem\n{\n}\n"
  },
  {
    "path": "src/PseudoTypes/CallableArray.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\n\n/**\n * Value Object representing the type 'callable-array'.\n *\n * @psalm-immutable\n */\nfinal class CallableArray extends Array_ implements PseudoType\n{\n    public function __construct()\n    {\n        parent::__construct(new Mixed_(), new Integer());\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Array_(new Mixed_(), new Integer());\n    }\n\n    public function __toString(): string\n    {\n        return 'callable-array';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/CallableString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'callable-string'.\n *\n * @psalm-immutable\n */\nfinal class CallableString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'callable-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ClassString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'class-string'.\n *\n * @psalm-immutable\n */\nfinal class ClassString extends String_ implements PseudoType\n{\n    /** @var Type|null */\n    private $genericType;\n\n    public function __construct(?Type $genericType = null)\n    {\n        $this->genericType = $genericType;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    public function getGenericType(): ?Type\n    {\n        return $this->genericType;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->genericType === null) {\n            return 'class-string';\n        }\n\n        return 'class-string<' . (string) $this->genericType . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ClosedResource.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Resource_;\n\n/**\n * Value Object representing the type 'closed-resource'.\n *\n * @psalm-immutable\n */\nfinal class ClosedResource extends Resource_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Resource_();\n    }\n\n    public function __toString(): string\n    {\n        return 'closed-resource';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/Conditional.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\n\nuse function sprintf;\n\n/**\n * Value Object representing the conditional type.\n *\n * @psalm-immutable\n */\nfinal class Conditional extends Mixed_ implements PseudoType\n{\n    /** @var bool */\n    private $negated;\n    /** @var Type */\n    private $subjectType;\n    /** @var Type */\n    private $targetType;\n    /** @var Type */\n    private $if;\n    /** @var Type */\n    private $else;\n\n    public function __construct(bool $negated, Type $subjectType, Type $targetType, Type $if, Type $else)\n    {\n        $this->negated = $negated;\n        $this->subjectType = $subjectType;\n        $this->targetType = $targetType;\n        $this->if = $if;\n        $this->else = $else;\n    }\n\n    public function isNegated(): bool\n    {\n        return $this->negated;\n    }\n\n    public function getSubjectType(): Type\n    {\n        return $this->subjectType;\n    }\n\n    public function getTargetType(): Type\n    {\n        return $this->targetType;\n    }\n\n    public function getIf(): Type\n    {\n        return $this->if;\n    }\n\n    public function getElse(): Type\n    {\n        return $this->else;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Mixed_();\n    }\n\n    public function __toString(): string\n    {\n        return sprintf(\n            '(%s %s %s ? %s : %s)',\n            (string) $this->subjectType,\n            $this->negated ? 'is not' : 'is',\n            (string) $this->targetType,\n            (string) $this->if,\n            (string) $this->else\n        );\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ConditionalForParameter.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\n\nuse function sprintf;\n\n/**\n * Value Object representing the conditional type for parameter.\n *\n * @psalm-immutable\n */\nfinal class ConditionalForParameter extends Mixed_ implements PseudoType\n{\n    /** @var bool */\n    private $negated;\n    /** @var string */\n    private $parameterName;\n    /** @var Type */\n    private $targetType;\n    /** @var Type */\n    private $if;\n    /** @var Type */\n    private $else;\n\n    public function __construct(bool $negated, string $parameterName, Type $targetType, Type $if, Type $else)\n    {\n        $this->negated = $negated;\n        $this->parameterName = $parameterName;\n        $this->targetType = $targetType;\n        $this->if = $if;\n        $this->else = $else;\n    }\n\n    public function isNegated(): bool\n    {\n        return $this->negated;\n    }\n\n    public function getParameterName(): string\n    {\n        return $this->parameterName;\n    }\n\n    public function getTargetType(): Type\n    {\n        return $this->targetType;\n    }\n\n    public function getIf(): Type\n    {\n        return $this->if;\n    }\n\n    public function getElse(): Type\n    {\n        return $this->else;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Mixed_();\n    }\n\n    public function __toString(): string\n    {\n        return sprintf(\n            '(%s %s %s ? %s : %s)',\n            '$' . $this->parameterName,\n            $this->negated ? 'is not' : 'is',\n            (string) $this->targetType,\n            (string) $this->if,\n            (string) $this->else\n        );\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ConstExpression.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\n\nuse function sprintf;\n\n/** @psalm-immutable */\nfinal class ConstExpression extends Mixed_ implements PseudoType\n{\n    /** @var Type */\n    private $owner;\n    /** @var string */\n    private $expression;\n\n    public function __construct(Type $owner, string $expression)\n    {\n        $this->owner = $owner;\n        $this->expression = $expression;\n    }\n\n    public function getOwner(): Type\n    {\n        return $this->owner;\n    }\n\n    public function getExpression(): string\n    {\n        return $this->expression;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Mixed_();\n    }\n\n    public function __toString(): string\n    {\n        return sprintf('%s::%s', (string) $this->owner, $this->expression);\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/EnumString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'enum-string'.\n *\n * @psalm-immutable\n */\nfinal class EnumString extends String_ implements PseudoType\n{\n    /** @var Type|null */\n    private $genericType;\n\n    public function __construct(?Type $genericType = null)\n    {\n        $this->genericType = $genericType;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    public function getGenericType(): ?Type\n    {\n        return $this->genericType;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->genericType === null) {\n            return 'enum-string';\n        }\n\n        return 'enum-string<' . (string) $this->genericType . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/False_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link https://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Boolean;\n\n/**\n * Value Object representing the PseudoType 'False', which is a Boolean type.\n *\n * @psalm-immutable\n */\nfinal class False_ extends Boolean implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Boolean();\n    }\n\n    public function __toString(): string\n    {\n        return 'false';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/FloatValue.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Float_;\n\n/** @psalm-immutable */\nclass FloatValue extends Float_ implements PseudoType\n{\n    /** @var float */\n    private $value;\n\n    public function __construct(float $value)\n    {\n        $this->value = $value;\n    }\n\n    public function getValue(): float\n    {\n        return $this->value;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Float_();\n    }\n\n    public function __toString(): string\n    {\n        return (string) $this->value;\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/Generic.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Object_;\n\nuse function implode;\n\n/**\n * Value Object representing a type with generics.\n *\n * @psalm-immutable\n */\nfinal class Generic extends Object_\n{\n    /** @var Type[] */\n    private $types;\n\n    /**\n     * @param Type[] $types\n     */\n    public function __construct(?Fqsen $fqsen, array $types)\n    {\n        parent::__construct($fqsen);\n\n        $this->types = $types;\n    }\n\n    /**\n     * @return Type[]\n     */\n    public function getTypes(): array\n    {\n        return $this->types;\n    }\n\n    public function __toString(): string\n    {\n        $objectType = (string) ($this->fqsen ?? 'object');\n\n        return $objectType . '<' . implode(', ', $this->types) . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/HtmlEscapedString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'html-escaped-string'.\n *\n * @psalm-immutable\n */\nfinal class HtmlEscapedString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'html-escaped-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/IntMask.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\nuse function implode;\n\n/**\n * Value Object representing the `int-mask` type.\n *\n * @psalm-immutable\n */\nfinal class IntMask extends Integer implements PseudoType\n{\n    /** @var Type[] */\n    private $types;\n\n    public function __construct(Type ...$types)\n    {\n        $this->types = $types;\n    }\n\n    /**\n     * @return Type[]\n     */\n    public function getTypes(): array\n    {\n        return $this->types;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    public function __toString(): string\n    {\n        return 'int-mask<' . implode(', ', $this->types) . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/IntMaskOf.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the `int-mask-of` type.\n *\n * @psalm-immutable\n */\nfinal class IntMaskOf extends Integer implements PseudoType\n{\n    /** @var Type */\n    private $type;\n\n    public function __construct(Type $type)\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): Type\n    {\n        return $this->type;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    public function __toString(): string\n    {\n        return 'int-mask-of<' . $this->type . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/IntegerRange.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'int'.\n *\n * @psalm-immutable\n */\nfinal class IntegerRange extends Integer implements PseudoType\n{\n    /** @var string */\n    private $minValue;\n\n    /** @var string */\n    private $maxValue;\n\n    public function __construct(string $minValue, string $maxValue)\n    {\n        $this->minValue = $minValue;\n        $this->maxValue = $maxValue;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    public function getMinValue(): string\n    {\n        return $this->minValue;\n    }\n\n    public function getMaxValue(): string\n    {\n        return $this->maxValue;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'int<' . $this->minValue . ', ' . $this->maxValue . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/IntegerValue.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/** @psalm-immutable */\nfinal class IntegerValue extends Integer implements PseudoType\n{\n    /** @var int */\n    private $value;\n\n    public function __construct(int $value)\n    {\n        $this->value = $value;\n    }\n\n    public function getValue(): int\n    {\n        return $this->value;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    public function __toString(): string\n    {\n        return (string) $this->value;\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/InterfaceString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type `interface-string`.\n *\n * @psalm-immutable\n */\nfinal class InterfaceString extends String_ implements PseudoType\n{\n    /** @var Type|null */\n    private $genericType;\n\n    public function __construct(?Type $genericType = null)\n    {\n        $this->genericType = $genericType;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    public function getGenericType(): ?Type\n    {\n        return $this->genericType;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->genericType === null) {\n            return 'interface-string';\n        }\n\n        return 'interface-string<' . (string) $this->genericType . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/KeyOf.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing the `key-of` type.\n *\n * @psalm-immutable\n */\nfinal class KeyOf extends ArrayKey implements PseudoType\n{\n    /** @var Type */\n    private $type;\n\n    public function __construct(Type $type)\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): Type\n    {\n        return $this->type;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new ArrayKey();\n    }\n\n    public function __toString(): string\n    {\n        return 'key-of<' . $this->type . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ListShape.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse function implode;\n\n/** @psalm-immutable */\nfinal class ListShape extends ArrayShape\n{\n    public function __toString(): string\n    {\n        return 'list{' . implode(', ', $this->getItems()) . '}';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ListShapeItem.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nfinal class ListShapeItem extends ArrayShapeItem\n{\n}\n"
  },
  {
    "path": "src/PseudoTypes/List_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'list'.\n *\n * @psalm-immutable\n */\nfinal class List_ extends Array_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Array_();\n    }\n\n    public function __construct(?Type $valueType = null)\n    {\n        parent::__construct($valueType, new Integer());\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->valueType === null) {\n            return 'list';\n        }\n\n        return 'list<' . $this->valueType . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/LiteralString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'literal-string'.\n *\n * @psalm-immutable\n */\nfinal class LiteralString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'literal-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/LowercaseString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'lowercase-string'.\n *\n * @psalm-immutable\n */\nfinal class LowercaseString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'lowercase-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NegativeInteger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'negative-int'.\n *\n * @psalm-immutable\n */\nfinal class NegativeInteger extends Integer implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'negative-int';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NeverReturn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Never_;\n\n/**\n * Value Object representing the type 'never-return'.\n *\n * @psalm-immutable\n */\nfinal class NeverReturn extends Never_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Never_();\n    }\n\n    public function __toString(): string\n    {\n        return 'never-return';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NeverReturns.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Never_;\n\n/**\n * Value Object representing the type 'never-returns'.\n *\n * @psalm-immutable\n */\nfinal class NeverReturns extends Never_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Never_();\n    }\n\n    public function __toString(): string\n    {\n        return 'never-returns';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NoReturn.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Never_;\n\n/**\n * Value Object representing the type 'no-return'.\n *\n * @psalm-immutable\n */\nfinal class NoReturn extends Never_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Never_();\n    }\n\n    public function __toString(): string\n    {\n        return 'no-return';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonEmptyArray.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Array_;\n\n/**\n * Value Object representing the type 'non-empty-array'.\n *\n * @psalm-immutable\n */\nfinal class NonEmptyArray extends Array_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Array_($this->valueType, $this->keyType);\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->valueType === null) {\n            return 'non-empty-array';\n        }\n\n        if ($this->keyType) {\n            return 'non-empty-array<' . $this->keyType . ', ' . $this->valueType . '>';\n        }\n\n        return 'non-empty-array<' . $this->valueType . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonEmptyList.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'non-empty-list'.\n *\n * @psalm-immutable\n */\nfinal class NonEmptyList extends Array_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Array_($this->valueType, $this->keyType);\n    }\n\n    public function __construct(?Type $valueType = null)\n    {\n        parent::__construct($valueType, new Integer());\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->valueType === null) {\n            return 'non-empty-list';\n        }\n\n        return 'non-empty-list<' . $this->valueType . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonEmptyLowercaseString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'non-empty-lowercase-string'.\n *\n * @psalm-immutable\n */\nfinal class NonEmptyLowercaseString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'non-empty-lowercase-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonEmptyString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'non-empty-string'.\n *\n * @psalm-immutable\n */\nfinal class NonEmptyString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'non-empty-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonFalsyString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'non-falsy-string'.\n *\n * @psalm-immutable\n */\nfinal class NonFalsyString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    public function __toString(): string\n    {\n        return 'non-falsy-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonNegativeInteger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'non-negative-int'.\n *\n * @psalm-immutable\n */\nfinal class NonNegativeInteger extends Integer implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'non-negative-int';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonPositiveInteger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'non-positive-int'.\n *\n * @psalm-immutable\n */\nfinal class NonPositiveInteger extends Integer implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    public function __toString(): string\n    {\n        return 'non-positive-int';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NonZeroInteger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'non-zero-int'.\n *\n * @psalm-immutable\n */\nfinal class NonZeroInteger extends Integer implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'non-zero-int';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/NumericString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'numeric-string'.\n *\n * @psalm-immutable\n */\nfinal class NumericString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'numeric-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/Numeric_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\AggregatedType;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Float_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the 'numeric' pseudo-type, which is either a numeric-string, integer or float.\n *\n * @psalm-immutable\n */\nfinal class Numeric_ extends AggregatedType implements PseudoType\n{\n    public function __construct()\n    {\n        AggregatedType::__construct([new NumericString(), new Integer(), new Float_()], '|');\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Compound([new NumericString(), new Integer(), new Float_()]);\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'numeric';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ObjectShape.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Object_;\n\nuse function implode;\n\n/** @psalm-immutable */\nfinal class ObjectShape extends Object_ implements PseudoType\n{\n    /** @var ObjectShapeItem[] */\n    private $items;\n\n    public function __construct(ObjectShapeItem ...$items)\n    {\n        $this->items = $items;\n    }\n\n    /**\n     * @return ObjectShapeItem[]\n     */\n    public function getItems(): array\n    {\n        return $this->items;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Object_();\n    }\n\n    public function __toString(): string\n    {\n        return 'object{' . implode(', ', $this->items) . '}';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ObjectShapeItem.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nfinal class ObjectShapeItem extends ShapeItem\n{\n}\n"
  },
  {
    "path": "src/PseudoTypes/OffsetAccess.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Callable_;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Nullable;\n\n/**\n * Value Object representing the offset access type.\n *\n * @psalm-immutable\n */\nfinal class OffsetAccess extends Mixed_ implements PseudoType\n{\n    /** @var Type */\n    private $type;\n    /** @var Type */\n    private $offset;\n\n    public function __construct(Type $type, Type $offset)\n    {\n        $this->type = $type;\n        $this->offset = $offset;\n    }\n\n    public function getType(): Type\n    {\n        return $this->type;\n    }\n\n    public function getOffset(): Type\n    {\n        return $this->offset;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Mixed_();\n    }\n\n    public function __toString(): string\n    {\n        if (\n            $this->type instanceof Callable_\n            || $this->type instanceof ConstExpression\n            || $this->type instanceof Nullable\n        ) {\n            return '(' . $this->type . ')[' . $this->offset . ']';\n        }\n\n        return $this->type . '[' . $this->offset . ']';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/OpenResource.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Resource_;\n\n/**\n * Value Object representing the type 'open-resource'.\n *\n * @psalm-immutable\n */\nfinal class OpenResource extends Resource_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Resource_();\n    }\n\n    public function __toString(): string\n    {\n        return 'open-resource';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/PositiveInteger.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Integer;\n\n/**\n * Value Object representing the type 'positive-int'.\n *\n * @psalm-immutable\n */\nfinal class PositiveInteger extends Integer implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Integer();\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'positive-int';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/PrivatePropertiesOf.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\n/**\n * Value Object representing the `private-properties-of` type.\n *\n * @psalm-immutable\n */\nfinal class PrivatePropertiesOf extends PropertiesOf\n{\n    public function __toString(): string\n    {\n        return 'private-properties-of<' . $this->type . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/PropertiesOf.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the `properties-of` type.\n *\n * @psalm-immutable\n */\nclass PropertiesOf extends Array_ implements PseudoType\n{\n    /** @var Type */\n    protected $type;\n\n    public function __construct(Type $type)\n    {\n        parent::__construct(new Mixed_(), new String_());\n\n        $this->type = $type;\n    }\n\n    public function getType(): Type\n    {\n        return $this->type;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Array_(new Mixed_(), new String_());\n    }\n\n    public function __toString(): string\n    {\n        return 'properties-of<' . $this->type . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ProtectedPropertiesOf.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\n/**\n * Value Object representing the `protected-properties-of` type.\n *\n * @psalm-immutable\n */\nfinal class ProtectedPropertiesOf extends PropertiesOf\n{\n    public function __toString(): string\n    {\n        return 'protected-properties-of<' . $this->type . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/PublicPropertiesOf.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\n/**\n * Value Object representing the `public-properties-of` type.\n *\n * @psalm-immutable\n */\nfinal class PublicPropertiesOf extends PropertiesOf\n{\n    public function __toString(): string\n    {\n        return 'public-properties-of<' . $this->type . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/Scalar.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Boolean;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Float_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean.\n *\n * @psalm-immutable\n */\nfinal class Scalar implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Compound([new String_(), new Integer(), new Float_(), new Boolean()]);\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'scalar';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ShapeItem.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\n\nuse function sprintf;\n\nabstract class ShapeItem\n{\n    /** @var string|null */\n    private $key;\n    /** @var Type */\n    private $value;\n    /** @var bool */\n    private $optional;\n\n    public function __construct(?string $key, ?Type $value, bool $optional)\n    {\n        $this->key = $key;\n        $this->value = $value ?? new Mixed_();\n        $this->optional = $optional;\n    }\n\n    public function getKey(): ?string\n    {\n        return $this->key;\n    }\n\n    public function getValue(): Type\n    {\n        return $this->value;\n    }\n\n    public function isOptional(): bool\n    {\n        return $this->optional;\n    }\n\n    public function __toString(): string\n    {\n        if ($this->key !== null && $this->key !== '') {\n            return sprintf(\n                '%s%s: %s',\n                $this->key,\n                $this->optional ? '?' : '',\n                (string) $this->value\n            );\n        }\n\n        return (string) $this->value;\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/StringValue.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\nuse function sprintf;\n\n/** @psalm-immutable */\nclass StringValue extends String_ implements PseudoType\n{\n    /** @var string */\n    private $value;\n\n    public function __construct(string $value)\n    {\n        $this->value = $value;\n    }\n\n    public function getValue(): string\n    {\n        return $this->value;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    public function __toString(): string\n    {\n        return sprintf('\"%s\"', $this->value);\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/TraitString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type `trait-string`.\n *\n * @psalm-immutable\n */\nfinal class TraitString extends String_ implements PseudoType\n{\n    /** @var Type|null */\n    private $genericType;\n\n    public function __construct(?Type $genericType = null)\n    {\n        $this->genericType = $genericType;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    public function getGenericType(): ?Type\n    {\n        return $this->genericType;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->genericType === null) {\n            return 'trait-string';\n        }\n\n        return 'trait-string<' . (string) $this->genericType . '>';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/True_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link https://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Boolean;\n\n/**\n * Value Object representing the PseudoType 'False', which is a Boolean type.\n *\n * @psalm-immutable\n */\nfinal class True_ extends Boolean implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new Boolean();\n    }\n\n    public function __toString(): string\n    {\n        return 'true';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/TruthyString.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\String_;\n\n/**\n * Value Object representing the type 'truthy-string'.\n *\n * @psalm-immutable\n */\nfinal class TruthyString extends String_ implements PseudoType\n{\n    public function underlyingType(): Type\n    {\n        return new String_();\n    }\n\n    public function __toString(): string\n    {\n        return 'truthy-string';\n    }\n}\n"
  },
  {
    "path": "src/PseudoTypes/ValueOf.php",
    "content": "<?php\n/*\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n *\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\PseudoType;\nuse phpDocumentor\\Reflection\\Type;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\n\n/**\n * Value Object representing the `value-of` type.\n *\n * @psalm-immutable\n */\nfinal class ValueOf extends Mixed_ implements PseudoType\n{\n    /** @var Type */\n    private $type;\n\n    public function __construct(Type $type)\n    {\n        $this->type = $type;\n    }\n\n    public function getType(): Type\n    {\n        return $this->type;\n    }\n\n    public function underlyingType(): Type\n    {\n        return new Mixed_();\n    }\n\n    public function __toString(): string\n    {\n        return 'value-of<' . $this->type . '>';\n    }\n}\n"
  },
  {
    "path": "src/Type.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\n/**\n * @psalm-immutable\n */\ninterface Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string;\n}\n"
  },
  {
    "path": "src/TypeResolver.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\nuse Doctrine\\Deprecations\\Deprecation;\nuse InvalidArgumentException;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayKey;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayShapeItem;\nuse phpDocumentor\\Reflection\\PseudoTypes\\CallableArray;\nuse phpDocumentor\\Reflection\\PseudoTypes\\CallableString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ClassString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ClosedResource;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Conditional;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ConditionalForParameter;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ConstExpression;\nuse phpDocumentor\\Reflection\\PseudoTypes\\EnumString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\False_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\FloatValue;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Generic;\nuse phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntegerValue;\nuse phpDocumentor\\Reflection\\PseudoTypes\\InterfaceString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntMask;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntMaskOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\KeyOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\List_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ListShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ListShapeItem;\nuse phpDocumentor\\Reflection\\PseudoTypes\\LiteralString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NeverReturn;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NeverReturns;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyArray;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyList;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonFalsyString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonNegativeInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonPositiveInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonZeroInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NoReturn;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Numeric_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NumericString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ObjectShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ObjectShapeItem;\nuse phpDocumentor\\Reflection\\PseudoTypes\\OffsetAccess;\nuse phpDocumentor\\Reflection\\PseudoTypes\\OpenResource;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PrivatePropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ProtectedPropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PublicPropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Scalar;\nuse phpDocumentor\\Reflection\\PseudoTypes\\StringValue;\nuse phpDocumentor\\Reflection\\PseudoTypes\\TraitString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\True_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\TruthyString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ValueOf;\nuse phpDocumentor\\Reflection\\Types\\AggregatedType;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Boolean;\nuse phpDocumentor\\Reflection\\Types\\Callable_;\nuse phpDocumentor\\Reflection\\Types\\CallableParameter;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse phpDocumentor\\Reflection\\Types\\Expression;\nuse phpDocumentor\\Reflection\\Types\\Float_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Intersection;\nuse phpDocumentor\\Reflection\\Types\\Iterable_;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Never_;\nuse phpDocumentor\\Reflection\\Types\\Null_;\nuse phpDocumentor\\Reflection\\Types\\Nullable;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\Parent_;\nuse phpDocumentor\\Reflection\\Types\\Resource_;\nuse phpDocumentor\\Reflection\\Types\\Self_;\nuse phpDocumentor\\Reflection\\Types\\Static_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse phpDocumentor\\Reflection\\Types\\This;\nuse phpDocumentor\\Reflection\\Types\\Void_;\nuse PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprFloatNode;\nuse PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode;\nuse PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode;\nuse PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstFetchNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeItemNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ArrayShapeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ArrayTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\CallableTypeParameterNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeForParameterNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ConditionalTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ConstTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\GenericTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\IntersectionTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\NullableTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeItemNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ObjectShapeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\OffsetAccessTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\ThisTypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode;\nuse PHPStan\\PhpDocParser\\Ast\\Type\\UnionTypeNode;\nuse PHPStan\\PhpDocParser\\Lexer\\Lexer;\nuse PHPStan\\PhpDocParser\\Parser\\ConstExprParser;\nuse PHPStan\\PhpDocParser\\Parser\\ParserException;\nuse PHPStan\\PhpDocParser\\Parser\\TokenIterator;\nuse PHPStan\\PhpDocParser\\Parser\\TypeParser;\nuse PHPStan\\PhpDocParser\\ParserConfig;\nuse RuntimeException;\n\nuse function array_key_exists;\nuse function array_map;\nuse function array_reverse;\nuse function class_exists;\nuse function class_implements;\nuse function get_class;\nuse function in_array;\nuse function sprintf;\nuse function strpos;\nuse function strtolower;\nuse function substr;\nuse function trim;\n\nfinal class TypeResolver\n{\n    /** @var string Definition of the NAMESPACE operator in PHP */\n    private const OPERATOR_NAMESPACE = '\\\\';\n\n    /**\n     * @var array<string, string> List of recognized keywords and unto which Value Object they map\n     * @psalm-var array<string, class-string<Type>>\n     */\n    private $keywords = [\n        'string' => String_::class,\n        'class-string' => ClassString::class,\n        'interface-string' => InterfaceString::class,\n        'html-escaped-string' => HtmlEscapedString::class,\n        'lowercase-string' => LowercaseString::class,\n        'non-empty-lowercase-string' => NonEmptyLowercaseString::class,\n        'non-empty-string' => NonEmptyString::class,\n        'numeric-string' => NumericString::class,\n        'numeric' => Numeric_::class,\n        'trait-string' => TraitString::class,\n        'enum-string' => EnumString::class,\n        'int' => Integer::class,\n        'integer' => Integer::class,\n        'positive-int' => PositiveInteger::class,\n        'negative-int' => NegativeInteger::class,\n        'bool' => Boolean::class,\n        'boolean' => Boolean::class,\n        'real' => Float_::class,\n        'float' => Float_::class,\n        'double' => Float_::class,\n        'object' => Object_::class,\n        'mixed' => Mixed_::class,\n        'array' => Array_::class,\n        'callable-array' => CallableArray::class,\n        'array-key' => ArrayKey::class,\n        'non-empty-array' => NonEmptyArray::class,\n        'resource' => Resource_::class,\n        'open-resource' => OpenResource::class,\n        'closed-resource' => ClosedResource::class,\n        'void' => Void_::class,\n        'null' => Null_::class,\n        'scalar' => Scalar::class,\n        'callback' => Callable_::class,\n        'callable' => Callable_::class,\n        'callable-string' => CallableString::class,\n        'false' => False_::class,\n        'true' => True_::class,\n        'literal-string' => LiteralString::class,\n        'self' => Self_::class,\n        '$this' => This::class,\n        'static' => Static_::class,\n        'parent' => Parent_::class,\n        'iterable' => Iterable_::class,\n        'never' => Never_::class,\n        'never-return' => NeverReturn::class,\n        'never-returns' => NeverReturns::class,\n        'no-return' => NoReturn::class,\n        'list' => List_::class,\n        'non-empty-list' => NonEmptyList::class,\n        'non-falsy-string' => NonFalsyString::class,\n        'truthy-string' => TruthyString::class,\n        'non-positive-int' => NonPositiveInteger::class,\n        'non-negative-int' => NonNegativeInteger::class,\n        'non-zero-int' => NonZeroInteger::class,\n    ];\n\n    /**\n     * @psalm-readonly\n     * @var FqsenResolver\n     */\n    private $fqsenResolver;\n    /**\n     * @psalm-readonly\n     * @var TypeParser\n     */\n    private $typeParser;\n    /**\n     * @psalm-readonly\n     * @var Lexer\n     */\n    private $lexer;\n\n    /**\n     * Initializes this TypeResolver with the means to create and resolve Fqsen objects.\n     */\n    public function __construct(?FqsenResolver $fqsenResolver = null)\n    {\n        $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver();\n        $this->typeParser = new TypeParser(new ParserConfig([]), new ConstExprParser(new ParserConfig([])));\n        $this->lexer = new Lexer(new ParserConfig([]));\n    }\n\n    /**\n     * Analyzes the given type and returns the FQCN variant.\n     *\n     * When a type is provided this method checks whether it is not a keyword or\n     * Fully Qualified Class Name. If so it will use the given namespace and\n     * aliases to expand the type to a FQCN representation.\n     *\n     * This method only works as expected if the namespace and aliases are set;\n     * no dynamic reflection is being performed here.\n     *\n     * @uses Context::getNamespace()        to determine with what to prefix the type name.\n     * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be\n     * replaced with another namespace.\n     *\n     * @param string $type The relative or absolute type.\n     */\n    public function resolve(string $type, ?Context $context = null): Type\n    {\n        $type = trim($type);\n        if (!$type) {\n            throw new InvalidArgumentException('Attempted to resolve \"' . $type . '\" but it appears to be empty');\n        }\n\n        if ($context === null) {\n            $context = new Context('');\n        }\n\n        $tokens = $this->lexer->tokenize($type);\n        $tokenIterator = new TokenIterator($tokens);\n\n        $ast = $this->parse($tokenIterator);\n        $type = $this->createType($ast, $context);\n\n        if (\n            $tokenIterator->isCurrentTokenType(Lexer::TOKEN_UNION) ||\n            $tokenIterator->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)\n        ) {\n            Deprecation::trigger(\n                'phpdocumentor/type-resolver',\n                'https://github.com/phpDocumentor/TypeResolver/issues/184',\n                'Legacy nullable type detected, please update your code as\n                you are using nullable types in a docblock. support is removed in v2.0.0'\n            );\n        }\n\n        return $type;\n    }\n\n    public function createType(?TypeNode $type, Context $context): Type\n    {\n        if ($type === null) {\n            return new Mixed_();\n        }\n\n        switch (get_class($type)) {\n            case ArrayTypeNode::class:\n                return new Array_(\n                    $this->createType($type->type, $context)\n                );\n\n            case ArrayShapeNode::class:\n                switch ($type->kind) {\n                    case ArrayShapeNode::KIND_ARRAY:\n                        return new ArrayShape(\n                            ...array_map(\n                                function (ArrayShapeItemNode $item) use ($context): ArrayShapeItem {\n                                    return new ArrayShapeItem(\n                                        $item->keyName !== null ? (string) $item->keyName : null,\n                                        $this->createType($item->valueType, $context),\n                                        $item->optional\n                                    );\n                                },\n                                $type->items\n                            )\n                        );\n\n                    case ArrayShapeNode::KIND_LIST:\n                        return new ListShape(\n                            ...array_map(\n                                function (ArrayShapeItemNode $item) use ($context): ListShapeItem {\n                                    return new ListShapeItem(\n                                        null,\n                                        $this->createType($item->valueType, $context),\n                                        $item->optional\n                                    );\n                                },\n                                $type->items\n                            )\n                        );\n\n                    default:\n                        throw new RuntimeException('Unsupported array shape kind');\n                }\n            case ObjectShapeNode::class:\n                return new ObjectShape(\n                    ...array_map(\n                        function (ObjectShapeItemNode $item) use ($context): ObjectShapeItem {\n                            return new ObjectShapeItem(\n                                (string) $item->keyName,\n                                $this->createType($item->valueType, $context),\n                                $item->optional\n                            );\n                        },\n                        $type->items\n                    )\n                );\n\n            case CallableTypeNode::class:\n                return $this->createFromCallable($type, $context);\n\n            case ConstTypeNode::class:\n                return $this->createFromConst($type, $context);\n\n            case GenericTypeNode::class:\n                return $this->createFromGeneric($type, $context);\n\n            case IdentifierTypeNode::class:\n                return $this->resolveSingleType($type->name, $context);\n\n            case IntersectionTypeNode::class:\n                return new Intersection(\n                    array_map(\n                        function (TypeNode $nestedType) use ($context): Type {\n                            $type = $this->createType($nestedType, $context);\n                            if ($type instanceof AggregatedType) {\n                                return new Expression($type);\n                            }\n\n                            return $type;\n                        },\n                        $type->types\n                    )\n                );\n\n            case NullableTypeNode::class:\n                $nestedType = $this->createType($type->type, $context);\n\n                return new Nullable($nestedType);\n\n            case UnionTypeNode::class:\n                return new Compound(\n                    array_map(\n                        function (TypeNode $nestedType) use ($context): Type {\n                            $type = $this->createType($nestedType, $context);\n                            if ($type instanceof AggregatedType) {\n                                return new Expression($type);\n                            }\n\n                            return $type;\n                        },\n                        $type->types\n                    )\n                );\n\n            case ThisTypeNode::class:\n                return new This();\n\n            case ConditionalTypeNode::class:\n                return new Conditional(\n                    $type->negated,\n                    $this->createType($type->subjectType, $context),\n                    $this->createType($type->targetType, $context),\n                    $this->createType($type->if, $context),\n                    $this->createType($type->else, $context),\n                );\n\n            case ConditionalTypeForParameterNode::class:\n                return new ConditionalForParameter(\n                    $type->negated,\n                    substr($type->parameterName, 1),\n                    $this->createType($type->targetType, $context),\n                    $this->createType($type->if, $context),\n                    $this->createType($type->else, $context),\n                );\n\n            case OffsetAccessTypeNode::class:\n                return new OffsetAccess(\n                    $this->createType($type->type, $context),\n                    $this->createType($type->offset, $context)\n                );\n\n            default:\n                return new Mixed_();\n        }\n    }\n\n    private function createFromGeneric(GenericTypeNode $type, Context $context): Type\n    {\n        switch (strtolower($type->type->name)) {\n            case 'array':\n                $genericTypes = array_reverse($this->createTypesByTypeNodes($type->genericTypes, $context));\n\n                return new Array_(...$genericTypes);\n\n            case 'non-empty-array':\n                $genericTypes = array_reverse($this->createTypesByTypeNodes($type->genericTypes, $context));\n\n                return new NonEmptyArray(...$genericTypes);\n\n            case 'class-string':\n                return new ClassString($this->createType($type->genericTypes[0], $context));\n\n            case 'interface-string':\n                return new InterfaceString($this->createType($type->genericTypes[0], $context));\n\n            case 'trait-string':\n                return new TraitString($this->createType($type->genericTypes[0], $context));\n\n            case 'enum-string':\n                return new EnumString($this->createType($type->genericTypes[0], $context));\n\n            case 'list':\n                return new List_(\n                    $this->createType($type->genericTypes[0], $context)\n                );\n\n            case 'non-empty-list':\n                return new NonEmptyList(\n                    $this->createType($type->genericTypes[0], $context)\n                );\n\n            case 'int':\n                if (isset($type->genericTypes[1]) === false) {\n                    throw new RuntimeException('int<min,max> has not the correct format');\n                }\n\n                return new IntegerRange((string) $type->genericTypes[0], (string) $type->genericTypes[1]);\n\n            case 'iterable':\n                return new Iterable_(...array_reverse($this->createTypesByTypeNodes($type->genericTypes, $context)));\n\n            case 'key-of':\n                return new KeyOf($this->createType($type->genericTypes[0], $context));\n\n            case 'value-of':\n                return new ValueOf($this->createType($type->genericTypes[0], $context));\n\n            case 'properties-of':\n                return new PropertiesOf($this->createType($type->genericTypes[0], $context));\n\n            case 'public-properties-of':\n                return new PublicPropertiesOf($this->createType($type->genericTypes[0], $context));\n\n            case 'protected-properties-of':\n                return new ProtectedPropertiesOf($this->createType($type->genericTypes[0], $context));\n\n            case 'private-properties-of':\n                return new PrivatePropertiesOf($this->createType($type->genericTypes[0], $context));\n\n            case 'int-mask':\n                return new IntMask(...$this->createTypesByTypeNodes($type->genericTypes, $context));\n\n            case 'int-mask-of':\n                return new IntMaskOf($this->createType($type->genericTypes[0], $context));\n\n            case 'static':\n                return new Static_(...$this->createTypesByTypeNodes($type->genericTypes, $context));\n\n            case 'self':\n                return new Self_(...$this->createTypesByTypeNodes($type->genericTypes, $context));\n\n            default:\n                $mainType = $this->createType($type->type, $context);\n                if ($mainType instanceof Object_ === false) {\n                    throw new RuntimeException(sprintf('%s is an unsupported generic', (string) $mainType));\n                }\n\n                $types = $this->createTypesByTypeNodes($type->genericTypes, $context);\n\n                return new Generic($mainType->getFqsen(), $types);\n        }\n    }\n\n    private function createFromCallable(CallableTypeNode $type, Context $context): Callable_\n    {\n        return new Callable_(\n            (string) $type->identifier,\n            array_map(\n                function (CallableTypeParameterNode $param) use ($context): CallableParameter {\n                    return new CallableParameter(\n                        $this->createType($param->type, $context),\n                        $param->parameterName !== '' ? trim($param->parameterName, '$') : null,\n                        $param->isReference,\n                        $param->isVariadic,\n                        $param->isOptional\n                    );\n                },\n                $type->parameters\n            ),\n            $this->createType($type->returnType, $context)\n        );\n    }\n\n    private function createFromConst(ConstTypeNode $type, Context $context): Type\n    {\n        switch (true) {\n            case $type->constExpr instanceof ConstExprIntegerNode:\n                return new IntegerValue((int) $type->constExpr->value);\n\n            case $type->constExpr instanceof ConstExprFloatNode:\n                return new FloatValue((float) $type->constExpr->value);\n\n            case $type->constExpr instanceof ConstExprStringNode:\n                return new StringValue($type->constExpr->value);\n\n            case $type->constExpr instanceof ConstFetchNode:\n                return new ConstExpression(\n                    $this->resolve($type->constExpr->className, $context),\n                    $type->constExpr->name\n                );\n\n            default:\n                throw new RuntimeException(sprintf('Unsupported constant type %s', get_class($type)));\n        }\n    }\n\n    /**\n     * resolve the given type into a type object\n     *\n     * @param string $type the type string, representing a single type\n     *\n     * @return Type|Array_|Object_\n     *\n     * @psalm-mutation-free\n     */\n    private function resolveSingleType(string $type, Context $context): object\n    {\n        switch (true) {\n            case $this->isKeyword($type):\n                return $this->resolveKeyword($type);\n\n            case $this->isFqsen($type):\n                return $this->resolveTypedObject($type);\n\n            case $this->isPartialStructuralElementName($type):\n                return $this->resolveTypedObject($type, $context);\n\n            // @codeCoverageIgnoreStart\n            default:\n                // I haven't got the foggiest how the logic would come here but added this as a defense.\n                throw new RuntimeException(\n                    'Unable to resolve type \"' . $type . '\", there is no known method to resolve it'\n                );\n        }\n\n        // @codeCoverageIgnoreEnd\n    }\n\n    /**\n     * Adds a keyword to the list of Keywords and associates it with a specific Value Object.\n     *\n     * @psalm-param class-string<Type> $typeClassName\n     */\n    public function addKeyword(string $keyword, string $typeClassName): void\n    {\n        if (!class_exists($typeClassName)) {\n            throw new InvalidArgumentException(\n                'The Value Object that needs to be created with a keyword \"' . $keyword . '\" must be an existing class'\n                . ' but we could not find the class ' . $typeClassName\n            );\n        }\n\n        $interfaces = class_implements($typeClassName);\n        if ($interfaces === false) {\n            throw new InvalidArgumentException(\n                'The Value Object that needs to be created with a keyword \"' . $keyword . '\" must be an existing class'\n                . ' but we could not find the class ' . $typeClassName\n            );\n        }\n\n        if (!in_array(Type::class, $interfaces, true)) {\n            throw new InvalidArgumentException(\n                'The class \"' . $typeClassName . '\" must implement the interface \"phpDocumentor\\Reflection\\Type\"'\n            );\n        }\n\n        $this->keywords[$keyword] = $typeClassName;\n    }\n\n    /**\n     * Detects whether the given type represents a PHPDoc keyword.\n     *\n     * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.\n     *\n     * @psalm-mutation-free\n     */\n    private function isKeyword(string $type): bool\n    {\n        return array_key_exists(strtolower($type), $this->keywords);\n    }\n\n    /**\n     * Detects whether the given type represents a relative structural element name.\n     *\n     * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.\n     *\n     * @psalm-mutation-free\n     */\n    private function isPartialStructuralElementName(string $type): bool\n    {\n        return (isset($type[0]) && $type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type);\n    }\n\n    /**\n     * Tests whether the given type is a Fully Qualified Structural Element Name.\n     *\n     * @psalm-mutation-free\n     */\n    private function isFqsen(string $type): bool\n    {\n        return strpos($type, self::OPERATOR_NAMESPACE) === 0;\n    }\n\n    /**\n     * Resolves the given keyword (such as `string`) into a Type object representing that keyword.\n     *\n     * @psalm-mutation-free\n     */\n    private function resolveKeyword(string $type): Type\n    {\n        $className = $this->keywords[strtolower($type)];\n\n        return new $className();\n    }\n\n    /**\n     * Resolves the given FQSEN string into an FQSEN object.\n     *\n     * @psalm-mutation-free\n     */\n    private function resolveTypedObject(string $type, ?Context $context = null): Object_\n    {\n        return new Object_($this->fqsenResolver->resolve($type, $context));\n    }\n\n    private function parse(TokenIterator $tokenIterator): TypeNode\n    {\n        try {\n            $ast = $this->typeParser->parse($tokenIterator);\n        } catch (ParserException $e) {\n            throw new RuntimeException($e->getMessage(), 0, $e);\n        }\n\n        return $ast;\n    }\n\n    /**\n     * @param TypeNode[] $nodes\n     *\n     * @return Type[]\n     */\n    private function createTypesByTypeNodes(array $nodes, Context $context): array\n    {\n        return array_map(\n            function (TypeNode $node) use ($context): Type {\n                return $this->createType($node, $context);\n            },\n            $nodes\n        );\n    }\n}\n"
  },
  {
    "path": "src/Types/AbstractList.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Represents a list of values. This is an abstract class for Array_ and List_.\n *\n * @psalm-immutable\n */\nabstract class AbstractList implements Type\n{\n    /** @var Type|null */\n    protected $valueType;\n\n    /** @var Type|null */\n    protected $keyType;\n\n    /** @var Type */\n    protected $defaultKeyType;\n\n    /** @var Type */\n    protected $defaultValueType;\n\n    /**\n     * Initializes this representation of an array with the given Type.\n     */\n    public function __construct(?Type $valueType = null, ?Type $keyType = null)\n    {\n        $this->defaultValueType = new Mixed_();\n        $this->valueType      = $valueType;\n        $this->defaultKeyType = new Compound([new String_(), new Integer()]);\n        $this->keyType        = $keyType;\n    }\n\n    public function getOriginalKeyType(): ?Type\n    {\n        return $this->keyType;\n    }\n\n    public function getOriginalValueType(): ?Type\n    {\n        return $this->valueType;\n    }\n\n    /**\n     * Returns the type for the keys of this array.\n     */\n    public function getKeyType(): Type\n    {\n        return $this->keyType ?? $this->defaultKeyType;\n    }\n\n    /**\n     * Returns the type for the values of this array.\n     */\n    public function getValueType(): Type\n    {\n        return $this->valueType ?? $this->defaultValueType;\n    }\n}\n"
  },
  {
    "path": "src/Types/AggregatedType.php",
    "content": "<?php\n/**\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse ArrayIterator;\nuse IteratorAggregate;\nuse phpDocumentor\\Reflection\\Type;\n\nuse function array_key_exists;\nuse function implode;\n\n/**\n * Base class for aggregated types like Compound and Intersection\n *\n * A Aggregated Type is not so much a special keyword or object reference but is a series of Types that are separated\n * using separator.\n *\n * @psalm-immutable\n * @template-implements IteratorAggregate<int, Type>\n */\nabstract class AggregatedType implements Type, IteratorAggregate\n{\n    /**\n     * @psalm-allow-private-mutation\n     * @var array<int, Type>\n     */\n    private $types = [];\n\n    /** @var string */\n    private $token;\n\n    /**\n     * @param array<Type> $types\n     */\n    public function __construct(array $types, string $token)\n    {\n        foreach ($types as $type) {\n            $this->add($type);\n        }\n\n        $this->token = $token;\n    }\n\n    /**\n     * Returns the type at the given index.\n     */\n    public function get(int $index): ?Type\n    {\n        if (!$this->has($index)) {\n            return null;\n        }\n\n        return $this->types[$index];\n    }\n\n    /**\n     * Tests if this compound type has a type with the given index.\n     */\n    public function has(int $index): bool\n    {\n        return array_key_exists($index, $this->types);\n    }\n\n    /**\n     * Tests if this compound type contains the given type.\n     */\n    public function contains(Type $type): bool\n    {\n        foreach ($this->types as $typePart) {\n            // if the type is duplicate; do not add it\n            if ((string) $typePart === (string) $type) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return implode($this->token, $this->types);\n    }\n\n    /**\n     * @return ArrayIterator<int, Type>\n     */\n    public function getIterator(): ArrayIterator\n    {\n        return new ArrayIterator($this->types);\n    }\n\n    /**\n     * @psalm-suppress ImpureMethodCall\n     */\n    private function add(Type $type): void\n    {\n        if ($type instanceof static) {\n            foreach ($type->getIterator() as $subType) {\n                $this->add($subType);\n            }\n\n            return;\n        }\n\n        // if the type is duplicate; do not add it\n        if ($this->contains($type)) {\n            return;\n        }\n\n        $this->types[] = $type;\n    }\n}\n"
  },
  {
    "path": "src/Types/Array_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse function preg_match;\nuse function substr;\n\n/**\n * Represents an array type as described in the PSR-5, the PHPDoc Standard.\n *\n * An array can be represented in two forms:\n *\n * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed_'.\n * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a\n *    type name.\n *\n * @psalm-immutable\n */\nclass Array_ extends AbstractList\n{\n    public function __toString(): string\n    {\n        if ($this->valueType === null) {\n            return 'array';\n        }\n\n        $valueTypeString = (string) $this->valueType;\n\n        if ($this->keyType) {\n            return 'array<' . $this->keyType . ', ' . $valueTypeString . '>';\n        }\n\n        if (!preg_match('/[^\\w\\\\\\\\]/', $valueTypeString) || substr($valueTypeString, -2, 2) === '[]') {\n            return $valueTypeString . '[]';\n        }\n\n        return 'array<' . $valueTypeString . '>';\n    }\n}\n"
  },
  {
    "path": "src/Types/Boolean.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing a Boolean type.\n *\n * @psalm-immutable\n */\nclass Boolean implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'bool';\n    }\n}\n"
  },
  {
    "path": "src/Types/CallableParameter.php",
    "content": "<?php\n/**\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\nuse function trim;\n\n/**\n * Value Object representing a Callable parameters.\n *\n * @psalm-immutable\n */\nfinal class CallableParameter\n{\n    /** @var Type */\n    private $type;\n\n    /** @var bool */\n    private $isReference;\n\n    /** @var bool */\n    private $isVariadic;\n\n    /** @var bool */\n    private $isOptional;\n\n    /** @var string|null */\n    private $name;\n\n    public function __construct(\n        Type $type,\n        ?string $name = null,\n        bool $isReference = false,\n        bool $isVariadic = false,\n        bool $isOptional = false\n    ) {\n        $this->type = $type;\n        $this->isReference = $isReference;\n        $this->isVariadic = $isVariadic;\n        $this->isOptional = $isOptional;\n        $this->name = $name;\n    }\n\n    public function getName(): ?string\n    {\n        return $this->name;\n    }\n\n    public function getType(): Type\n    {\n        return $this->type;\n    }\n\n    public function isReference(): bool\n    {\n        return $this->isReference;\n    }\n\n    public function isVariadic(): bool\n    {\n        return $this->isVariadic;\n    }\n\n    public function isOptional(): bool\n    {\n        return $this->isOptional;\n    }\n\n    public function __toString(): string\n    {\n        $reference = $this->isReference ? '&' : '';\n        $variadic = $this->isVariadic ? '...' : '';\n        $optional = $this->isOptional ? '=' : '';\n        $name = $this->name !== null ? '$' . $this->name : '';\n\n        return trim($this->type . ' ' . $reference . $variadic . $name . $optional);\n    }\n}\n"
  },
  {
    "path": "src/Types/Callable_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\nuse function implode;\n\n/**\n * Value Object representing a Callable type.\n *\n * @psalm-immutable\n */\nfinal class Callable_ implements Type\n{\n    /** @var string */\n    private $identifier;\n    /** @var Type|null */\n    private $returnType;\n    /** @var CallableParameter[] */\n    private $parameters;\n\n    /**\n     * @param CallableParameter[] $parameters\n     */\n    public function __construct(\n        string $identifier = 'callable',\n        array $parameters = [],\n        ?Type $returnType = null\n    ) {\n        $this->identifier = $identifier;\n        $this->parameters = $parameters;\n        $this->returnType = $returnType;\n    }\n\n    public function getIdentifier(): string\n    {\n        return $this->identifier;\n    }\n\n    /** @return CallableParameter[] */\n    public function getParameters(): array\n    {\n        return $this->parameters;\n    }\n\n    public function getReturnType(): ?Type\n    {\n        return $this->returnType;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if (!$this->parameters && $this->returnType === null) {\n            return $this->identifier;\n        }\n\n        if ($this->returnType instanceof self) {\n            $returnType = '(' . (string) $this->returnType . ')';\n        } else {\n            $returnType = (string) $this->returnType;\n        }\n\n        return $this->identifier . '(' . implode(', ', $this->parameters) . '): ' . $returnType;\n    }\n}\n"
  },
  {
    "path": "src/Types/Compound.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing a Compound Type.\n *\n * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated\n * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type\n * may contain a value with any of the given types.\n *\n * @psalm-immutable\n */\nfinal class Compound extends AggregatedType\n{\n    /**\n     * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface.\n     *\n     * @param array<Type> $types\n     */\n    public function __construct(array $types)\n    {\n        parent::__construct($types, '|');\n    }\n}\n"
  },
  {
    "path": "src/Types/Context.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse function strlen;\nuse function substr;\nuse function trim;\n\n/**\n * Provides information about the Context in which the DocBlock occurs that receives this context.\n *\n * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable\n * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since\n * you can provide a short form or make use of namespace aliases.\n *\n * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your\n * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in\n * which an associated class resides for its namespace and imports.\n *\n * @see ContextFactory::createFromReflector()\n * @see ContextFactory::createForNamespace()\n *\n * @psalm-immutable\n */\nfinal class Context\n{\n    /** @var string The current namespace. */\n    private $namespace;\n\n    /**\n     * @var string[] List of namespace aliases => Fully Qualified Namespace.\n     * @psalm-var array<string, string>\n     */\n    private $namespaceAliases;\n\n    /**\n     * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN)\n     * format (without a preceding `\\`).\n     *\n     * @param string   $namespace        The namespace where this DocBlock resides in.\n     * @param string[] $namespaceAliases List of namespace aliases => Fully Qualified Namespace.\n     * @psalm-param array<string, string> $namespaceAliases\n     */\n    public function __construct(string $namespace, array $namespaceAliases = [])\n    {\n        $this->namespace = $namespace !== 'global' && $namespace !== 'default'\n            ? trim($namespace, '\\\\')\n            : '';\n\n        foreach ($namespaceAliases as $alias => $fqnn) {\n            if ($fqnn[0] === '\\\\') {\n                $fqnn = substr($fqnn, 1);\n            }\n\n            if ($fqnn[strlen($fqnn) - 1] === '\\\\') {\n                $fqnn = substr($fqnn, 0, -1);\n            }\n\n            $namespaceAliases[$alias] = $fqnn;\n        }\n\n        $this->namespaceAliases = $namespaceAliases;\n    }\n\n    /**\n     * Returns the Qualified Namespace Name (thus without `\\` in front) where the associated element is in.\n     */\n    public function getNamespace(): string\n    {\n        return $this->namespace;\n    }\n\n    /**\n     * Returns a list of Qualified Namespace Names (thus without `\\` in front) that are imported, the keys represent\n     * the alias for the imported Namespace.\n     *\n     * @return string[]\n     * @psalm-return array<string, string>\n     */\n    public function getNamespaceAliases(): array\n    {\n        return $this->namespaceAliases;\n    }\n}\n"
  },
  {
    "path": "src/Types/ContextFactory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse ArrayIterator;\nuse InvalidArgumentException;\nuse ReflectionClass;\nuse ReflectionClassConstant;\nuse ReflectionMethod;\nuse ReflectionParameter;\nuse ReflectionProperty;\nuse Reflector;\nuse RuntimeException;\nuse UnexpectedValueException;\n\nuse function define;\nuse function defined;\nuse function file_exists;\nuse function file_get_contents;\nuse function get_class;\nuse function in_array;\nuse function is_string;\nuse function strrpos;\nuse function substr;\nuse function token_get_all;\nuse function trim;\n\nuse const T_AS;\nuse const T_CLASS;\nuse const T_CURLY_OPEN;\nuse const T_DOLLAR_OPEN_CURLY_BRACES;\nuse const T_NAME_FULLY_QUALIFIED;\nuse const T_NAME_QUALIFIED;\nuse const T_NAMESPACE;\nuse const T_NS_SEPARATOR;\nuse const T_STRING;\nuse const T_TRAIT;\nuse const T_USE;\n\nif (!defined('T_NAME_QUALIFIED')) {\n    define('T_NAME_QUALIFIED', 10001);\n}\n\nif (!defined('T_NAME_FULLY_QUALIFIED')) {\n    define('T_NAME_FULLY_QUALIFIED', 10002);\n}\n\n/**\n * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor.\n *\n * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to\n * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to\n * Fully Qualified names.\n *\n * @see Context for more information.\n */\nfinal class ContextFactory\n{\n    /** The literal used at the end of a use statement. */\n    private const T_LITERAL_END_OF_USE = ';';\n\n    /** The literal used between sets of use statements */\n    private const T_LITERAL_USE_SEPARATOR = ',';\n\n    /**\n     * Build a Context given a Class Reflection.\n     *\n     * @see Context for more information on Contexts.\n     */\n    public function createFromReflector(Reflector $reflector): Context\n    {\n        if ($reflector instanceof ReflectionClass) {\n            //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable\n            /** @var ReflectionClass<object> $reflector */\n\n            return $this->createFromReflectionClass($reflector);\n        }\n\n        if ($reflector instanceof ReflectionParameter) {\n            return $this->createFromReflectionParameter($reflector);\n        }\n\n        if ($reflector instanceof ReflectionMethod) {\n            return $this->createFromReflectionMethod($reflector);\n        }\n\n        if ($reflector instanceof ReflectionProperty) {\n            return $this->createFromReflectionProperty($reflector);\n        }\n\n        if ($reflector instanceof ReflectionClassConstant) {\n            return $this->createFromReflectionClassConstant($reflector);\n        }\n\n        throw new UnexpectedValueException('Unhandled \\Reflector instance given:  ' . get_class($reflector));\n    }\n\n    private function createFromReflectionParameter(ReflectionParameter $parameter): Context\n    {\n        $class = $parameter->getDeclaringClass();\n        if (!$class) {\n            throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName());\n        }\n\n        return $this->createFromReflectionClass($class);\n    }\n\n    private function createFromReflectionMethod(ReflectionMethod $method): Context\n    {\n        $class = $method->getDeclaringClass();\n\n        return $this->createFromReflectionClass($class);\n    }\n\n    private function createFromReflectionProperty(ReflectionProperty $property): Context\n    {\n        $class = $property->getDeclaringClass();\n\n        return $this->createFromReflectionClass($class);\n    }\n\n    private function createFromReflectionClassConstant(ReflectionClassConstant $constant): Context\n    {\n        //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable\n        /** @phpstan-var ReflectionClass<object> $class */\n        $class = $constant->getDeclaringClass();\n\n        return $this->createFromReflectionClass($class);\n    }\n\n    /**\n     * @phpstan-param ReflectionClass<object> $class\n     */\n    private function createFromReflectionClass(ReflectionClass $class): Context\n    {\n        $fileName  = $class->getFileName();\n        $namespace = $class->getNamespaceName();\n\n        if (is_string($fileName) && file_exists($fileName)) {\n            $contents = file_get_contents($fileName);\n            if ($contents === false) {\n                throw new RuntimeException('Unable to read file \"' . $fileName . '\"');\n            }\n\n            return $this->createForNamespace($namespace, $contents);\n        }\n\n        return new Context($namespace, []);\n    }\n\n    /**\n     * Build a Context for a namespace in the provided file contents.\n     *\n     * @see Context for more information on Contexts.\n     *\n     * @param string $namespace    It does not matter if a `\\` precedes the namespace name,\n     * this method first normalizes.\n     * @param string $fileContents The file's contents to retrieve the aliases from with the given namespace.\n     */\n    public function createForNamespace(string $namespace, string $fileContents): Context\n    {\n        $namespace        = trim($namespace, '\\\\');\n        $useStatements    = [];\n        $currentNamespace = '';\n        $tokens           = new ArrayIterator(token_get_all($fileContents));\n\n        while ($tokens->valid()) {\n            $currentToken = $tokens->current();\n            switch ($currentToken[0]) {\n                case T_NAMESPACE:\n                    $currentNamespace = $this->parseNamespace($tokens);\n                    break;\n                case T_CLASS:\n                case T_TRAIT:\n                    // Fast-forward the iterator through the class so that any\n                    // T_USE tokens found within are skipped - these are not\n                    // valid namespace use statements so should be ignored.\n                    $braceLevel      = 0;\n                    $firstBraceFound = false;\n                    while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) {\n                        $currentToken = $tokens->current();\n                        if (\n                            $currentToken === '{'\n                            || in_array($currentToken[0], [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true)\n                        ) {\n                            if (!$firstBraceFound) {\n                                $firstBraceFound = true;\n                            }\n\n                            ++$braceLevel;\n                        }\n\n                        if ($currentToken === '}') {\n                            --$braceLevel;\n                        }\n\n                        $tokens->next();\n                    }\n\n                    break;\n                case T_USE:\n                    if ($currentNamespace === $namespace) {\n                        $useStatements += $this->parseUseStatement($tokens);\n                    }\n\n                    break;\n            }\n\n            $tokens->next();\n        }\n\n        return new Context($namespace, $useStatements);\n    }\n\n    /**\n     * Deduce the name from tokens when we are at the T_NAMESPACE token.\n     *\n     * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens\n     */\n    private function parseNamespace(ArrayIterator $tokens): string\n    {\n        // skip to the first string or namespace separator\n        $this->skipToNextStringOrNamespaceSeparator($tokens);\n\n        $name = '';\n        $acceptedTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED];\n        while ($tokens->valid() && in_array($tokens->current()[0], $acceptedTokens, true)) {\n            $name .= $tokens->current()[1];\n            $tokens->next();\n        }\n\n        return $name;\n    }\n\n    /**\n     * Deduce the names of all imports when we are at the T_USE token.\n     *\n     * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens\n     *\n     * @return string[]\n     * @psalm-return array<string, string>\n     */\n    private function parseUseStatement(ArrayIterator $tokens): array\n    {\n        $uses = [];\n\n        while ($tokens->valid()) {\n            $this->skipToNextStringOrNamespaceSeparator($tokens);\n\n            $uses += $this->extractUseStatements($tokens);\n            $currentToken = $tokens->current();\n            if ($currentToken[0] === self::T_LITERAL_END_OF_USE) {\n                return $uses;\n            }\n        }\n\n        return $uses;\n    }\n\n    /**\n     * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.\n     *\n     * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens\n     */\n    private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens): void\n    {\n        while ($tokens->valid()) {\n            $currentToken = $tokens->current();\n            if (in_array($currentToken[0], [T_STRING, T_NS_SEPARATOR], true)) {\n                break;\n            }\n\n            if ($currentToken[0] === T_NAME_QUALIFIED) {\n                break;\n            }\n\n            if (defined('T_NAME_FULLY_QUALIFIED') && $currentToken[0] === T_NAME_FULLY_QUALIFIED) {\n                break;\n            }\n\n            $tokens->next();\n        }\n    }\n\n    /**\n     * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of\n     * a USE statement yet. This will return a key/value array of the alias => namespace.\n     *\n     * @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens\n     *\n     * @return string[]\n     * @psalm-return array<string, string>\n     *\n     * @psalm-suppress TypeDoesNotContainType\n     */\n    private function extractUseStatements(ArrayIterator $tokens): array\n    {\n        $extractedUseStatements = [];\n        $groupedNs              = '';\n        $currentNs              = '';\n        $currentAlias           = '';\n        $state                  = 'start';\n\n        while ($tokens->valid()) {\n            $currentToken = $tokens->current();\n            $tokenId      = is_string($currentToken) ? $currentToken : $currentToken[0];\n            $tokenValue   = is_string($currentToken) ? null : $currentToken[1];\n            switch ($state) {\n                case 'start':\n                    switch ($tokenId) {\n                        case T_STRING:\n                        case T_NS_SEPARATOR:\n                            $currentNs   .= (string) $tokenValue;\n                            $currentAlias =  $tokenValue;\n                            break;\n                        case T_NAME_QUALIFIED:\n                        case T_NAME_FULLY_QUALIFIED:\n                            $currentNs   .= (string) $tokenValue;\n                            $currentAlias = substr(\n                                (string) $tokenValue,\n                                (int) (strrpos((string) $tokenValue, '\\\\')) + 1\n                            );\n                            break;\n                        case T_CURLY_OPEN:\n                        case '{':\n                            $state     = 'grouped';\n                            $groupedNs = $currentNs;\n                            break;\n                        case T_AS:\n                            $state = 'start-alias';\n                            break;\n                        case self::T_LITERAL_USE_SEPARATOR:\n                        case self::T_LITERAL_END_OF_USE:\n                            $state = 'end';\n                            break;\n                        default:\n                            break;\n                    }\n\n                    break;\n                case 'start-alias':\n                    switch ($tokenId) {\n                        case T_STRING:\n                            $currentAlias = $tokenValue;\n                            break;\n                        case self::T_LITERAL_USE_SEPARATOR:\n                        case self::T_LITERAL_END_OF_USE:\n                            $state = 'end';\n                            break;\n                        default:\n                            break;\n                    }\n\n                    break;\n                case 'grouped':\n                    switch ($tokenId) {\n                        case T_STRING:\n                        case T_NS_SEPARATOR:\n                            $currentNs   .= (string) $tokenValue;\n                            $currentAlias = $tokenValue;\n                            break;\n                        case T_AS:\n                            $state = 'grouped-alias';\n                            break;\n                        case self::T_LITERAL_USE_SEPARATOR:\n                            $state                                          = 'grouped';\n                            $extractedUseStatements[(string) $currentAlias] = $currentNs;\n                            $currentNs                                      = $groupedNs;\n                            $currentAlias                                   = '';\n                            break;\n                        case self::T_LITERAL_END_OF_USE:\n                            $state = 'end';\n                            break;\n                        default:\n                            break;\n                    }\n\n                    break;\n                case 'grouped-alias':\n                    switch ($tokenId) {\n                        case T_STRING:\n                            $currentAlias = $tokenValue;\n                            break;\n                        case self::T_LITERAL_USE_SEPARATOR:\n                            $state                                          = 'grouped';\n                            $extractedUseStatements[(string) $currentAlias] = $currentNs;\n                            $currentNs                                      = $groupedNs;\n                            $currentAlias                                   = '';\n                            break;\n                        case self::T_LITERAL_END_OF_USE:\n                            $state = 'end';\n                            break;\n                        default:\n                            break;\n                    }\n            }\n\n            if ($state === 'end') {\n                break;\n            }\n\n            $tokens->next();\n        }\n\n        if ($groupedNs !== $currentNs) {\n            $extractedUseStatements[(string) $currentAlias] = $currentNs;\n        }\n\n        return $extractedUseStatements;\n    }\n}\n"
  },
  {
    "path": "src/Types/Expression.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Represents an expression type as described in the PSR-5, the PHPDoc Standard.\n *\n * @psalm-immutable\n */\nfinal class Expression implements Type\n{\n    /** @var Type */\n    protected $valueType;\n\n    /**\n     * Initializes this representation of an array with the given Type.\n     */\n    public function __construct(Type $valueType)\n    {\n        $this->valueType = $valueType;\n    }\n\n    /**\n     * Returns the value for the keys of this array.\n     */\n    public function getValueType(): Type\n    {\n        return $this->valueType;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return '(' . $this->valueType . ')';\n    }\n}\n"
  },
  {
    "path": "src/Types/Float_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing a Float.\n *\n * @psalm-immutable\n */\nclass Float_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'float';\n    }\n}\n"
  },
  {
    "path": "src/Types/Integer.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value object representing Integer type\n *\n * @psalm-immutable\n */\nclass Integer implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'int';\n    }\n}\n"
  },
  {
    "path": "src/Types/Intersection.php",
    "content": "<?php\n/**\n * This file is part of phpDocumentor.\n *\n *  For the full copyright and license information, please view the LICENSE\n *  file that was distributed with this source code.\n *\n *  @link      http://phpdoc.org\n */\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing a Intersection Type.\n *\n * A Intersection Type is not so much a special keyword or object reference but is a series of Types that are separated\n * using an AND operator (`&`). This combination of types signifies that whatever is associated with this Intersection\n * type may contain a value with any of the given types.\n *\n * @psalm-immutable\n */\nfinal class Intersection extends AggregatedType\n{\n    /**\n     * Initializes a intersection type (i.e. `\\A&\\B`) and tests if the provided types all implement the Type interface.\n     *\n     * @param array<Type> $types\n     */\n    public function __construct(array $types)\n    {\n        parent::__construct($types, '&');\n    }\n}\n"
  },
  {
    "path": "src/Types/Iterable_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\n/**\n * Value Object representing iterable type\n *\n * @psalm-immutable\n */\nfinal class Iterable_ extends AbstractList\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->valueType === null) {\n            return 'iterable';\n        }\n\n        if ($this->keyType) {\n            return 'iterable<' . $this->keyType . ', ' . $this->valueType . '>';\n        }\n\n        return 'iterable<' . $this->valueType . '>';\n    }\n}\n"
  },
  {
    "path": "src/Types/Mixed_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing an unknown, or mixed, type.\n *\n * @psalm-immutable\n */\nclass Mixed_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'mixed';\n    }\n}\n"
  },
  {
    "path": "src/Types/Never_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing the return-type 'never'.\n *\n * Never is generally only used when working with return types as it signifies that the method that only\n * ever throw or exit.\n *\n * @psalm-immutable\n */\nclass Never_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'never';\n    }\n}\n"
  },
  {
    "path": "src/Types/Null_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing a null value or type.\n *\n * @psalm-immutable\n */\nfinal class Null_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'null';\n    }\n}\n"
  },
  {
    "path": "src/Types/Nullable.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing a nullable type. The real type is wrapped.\n *\n * @psalm-immutable\n */\nfinal class Nullable implements Type\n{\n    /** @var Type The actual type that is wrapped */\n    private $realType;\n\n    /**\n     * Initialises this nullable type using the real type embedded\n     */\n    public function __construct(Type $realType)\n    {\n        $this->realType = $realType;\n    }\n\n    /**\n     * Provide access to the actual type directly, if needed.\n     */\n    public function getActualType(): Type\n    {\n        return $this->realType;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return '?' . $this->realType->__toString();\n    }\n}\n"
  },
  {
    "path": "src/Types/Object_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse InvalidArgumentException;\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Type;\n\nuse function strpos;\n\n/**\n * Value Object representing an object.\n *\n * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN,\n * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects\n * in general.\n *\n * @psalm-immutable\n */\nclass Object_ implements Type\n{\n    /** @var Fqsen|null */\n    protected $fqsen;\n\n    /**\n     * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'.\n     *\n     * @throws InvalidArgumentException When provided $fqsen is not a valid type.\n     */\n    public function __construct(?Fqsen $fqsen = null)\n    {\n        if (strpos((string) $fqsen, '::') !== false || strpos((string) $fqsen, '()') !== false) {\n            throw new InvalidArgumentException(\n                'Object types can only refer to a class, interface or trait but a method, function, constant or '\n                . 'property was received: ' . (string) $fqsen\n            );\n        }\n\n        $this->fqsen = $fqsen;\n    }\n\n    /**\n     * Returns the FQSEN associated with this object.\n     */\n    public function getFqsen(): ?Fqsen\n    {\n        return $this->fqsen;\n    }\n\n    public function __toString(): string\n    {\n        if ($this->fqsen) {\n            return (string) $this->fqsen;\n        }\n\n        return 'object';\n    }\n}\n"
  },
  {
    "path": "src/Types/Parent_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing the 'parent' type.\n *\n * Parent, as a Type, represents the parent class of class in which the associated element was defined.\n *\n * @psalm-immutable\n */\nfinal class Parent_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'parent';\n    }\n}\n"
  },
  {
    "path": "src/Types/Resource_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing the 'resource' Type.\n *\n * @psalm-immutable\n */\nclass Resource_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'resource';\n    }\n}\n"
  },
  {
    "path": "src/Types/Self_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\nuse function implode;\n\n/**\n * Value Object representing the 'self' type.\n *\n * Self, as a Type, represents the class in which the associated element was defined.\n *\n * @psalm-immutable\n */\nfinal class Self_ implements Type\n{\n    /** @var Type[] */\n    private $genericTypes;\n\n    public function __construct(Type ...$genericTypes)\n    {\n        $this->genericTypes = $genericTypes;\n    }\n\n    /**\n     * @return Type[]\n     */\n    public function getGenericTypes(): array\n    {\n        return $this->genericTypes;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->genericTypes) {\n            return 'self<' . implode(', ', $this->genericTypes) . '>';\n        }\n\n        return 'self';\n    }\n}\n"
  },
  {
    "path": "src/Types/Static_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\nuse function implode;\n\n/**\n * Value Object representing the 'static' type.\n *\n * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does\n * not take inheritance into account but static means that the return type is always that of the class of the called\n * element.\n *\n * See the documentation on late static binding in the PHP Documentation for more information on the difference between\n * static and self.\n *\n * @psalm-immutable\n */\nfinal class Static_ implements Type\n{\n    /** @var Type[] */\n    private $genericTypes;\n\n    public function __construct(Type ...$genericTypes)\n    {\n        $this->genericTypes = $genericTypes;\n    }\n\n    /**\n     * @return Type[]\n     */\n    public function getGenericTypes(): array\n    {\n        return $this->genericTypes;\n    }\n\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        if ($this->genericTypes) {\n            return 'static<' . implode(', ', $this->genericTypes) . '>';\n        }\n\n        return 'static';\n    }\n}\n"
  },
  {
    "path": "src/Types/String_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing the type 'string'.\n *\n * @psalm-immutable\n */\nclass String_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'string';\n    }\n}\n"
  },
  {
    "path": "src/Types/This.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing the '$this' pseudo-type.\n *\n * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is\n * commonly used when documenting fluent interfaces since it represents that the same object is returned.\n *\n * @psalm-immutable\n */\nfinal class This implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return '$this';\n    }\n}\n"
  },
  {
    "path": "src/Types/Void_.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Type;\n\n/**\n * Value Object representing the return-type 'void'.\n *\n * Void is generally only used when working with return types as it signifies that the method intentionally does not\n * return any value.\n *\n * @psalm-immutable\n */\nfinal class Void_ implements Type\n{\n    /**\n     * Returns a rendered output of the Type as it would be used in a DocBlock.\n     */\n    public function __toString(): string\n    {\n        return 'void';\n    }\n}\n"
  },
  {
    "path": "tests/benchmark/Assets/mpdf.php",
    "content": "<?php\n//Note that this call has been uglified to increase the performance hit during the benchmarks!\nnamespace Mpdf;\n\nuse Mpdf\\Config\\ConfigVariables;\nuse Mpdf\\Config\\FontVariables;\n\nuse Mpdf\\Conversion;\nuse Mpdf\\Strict;\n\nuse Mpdf\\Css\\Border, Mpdf\\Css\\TextVars;\n\nuse Mpdf\\Log\\Context as LogContext;\n\nuse Mpdf\\Fonts\\MetricsGenerator;\n\nuse Mpdf\\Output\\Destination;\n\nuse Mpdf\\QrCode;\n\nuse Mpdf\\{\n    Utils\\Arrays,\n           Utils\\NumericString,\n           Utils\\UtfString\n           };\n\nuse Psr\\Log\\LoggerInterface;\nuse Psr\\Log\\NullLogger;\n\n\nuse Composer\\Factory;\nuse Composer\\Installer;\nuse Composer\\IO\\ConsoleIO;\nuse PackageVersions\\Versions;\nuse AsetOfRandom\\Imports\\Command;\nuse AsetOfRandom\\Imports\\CompareClasses;\nuse AsetOfRandom\\Imports\\DetectChanges\\BCBreak\\ClassBased;\nuse AsetOfRandom\\Imports\\DetectChanges\\BCBreak\\ClassConstantBased;\nuse AsetOfRandom\\Imports\\DetectChanges\\BCBreak\\FunctionBased;\nuse AsetOfRandom\\Imports\\DetectChanges\\BCBreak\\InterfaceBased;\nuse AsetOfRandom\\Imports\\DetectChanges\\BCBreak\\MethodBased;\nuse AsetOfRandom\\Imports\\DetectChanges\\BCBreak\\PropertyBased;\nuse AsetOfRandom\\Imports\\DetectChanges\\BCBreak\\TraitBased;\nuse AsetOfRandom\\Imports\\DetectChanges\\Variance\\TypeIsContravariant;\nuse AsetOfRandom\\Imports\\DetectChanges\\Variance\\TypeIsCovariant;\nuse AsetOfRandom\\Imports\\Factory\\ComposerInstallationReflectorFactory;\nuse AsetOfRandom\\Imports\\Git\\GetVersionCollectionFromGitRepository;\nuse AsetOfRandom\\Imports\\Git\\GitCheckoutRevisionToTemporaryPath;\nuse AsetOfRandom\\Imports\\Git\\GitParseRevision;\nuse AsetOfRandom\\Imports\\Git\\PickLastMinorVersionFromCollection;\nuse AsetOfRandom\\Imports\\LocateDependencies\\LocateDependenciesViaComposer;\nuse AsetOfRandom\\Imports\\LocateSources\\LocateSourcesViaComposerJson;\nuse AsetOfRandom\\Imports\\BetterReflection;\nuse RuntimeException;\nuse Symfony\\Component\\Console\\Application;\nuse Symfony\\Component\\Console\\Input\\ArgvInput;\nuse Symfony\\Component\\Console\\Output\\ConsoleOutput;\n\n/**\n * mPDF, PHP library generating PDF files from UTF-8 encoded HTML\n *\n * based on FPDF by Olivier Plathey\n *      and HTML2FPDF by Renato Coelho\n *\n * @license GPL-2.0\n */\nclass Mpdf\n{\n    const VERSION = '8.0.6';\n\n    const SCALE = 72 / 25.4;\n\n    var $useFixedNormalLineHeight; // mPDF 6\n    var $useFixedTextBaseline; // mPDF 6\n    var $adjustFontDescLineheight; // mPDF 6\n    var $interpolateImages; // mPDF 6\n    var $defaultPagebreakType; // mPDF 6 pagebreaktype\n    var $indexUseSubentries; // mPDF 6\n\n    var $autoScriptToLang; // mPDF 6\n    var $baseScript; // mPDF 6\n    var $autoVietnamese; // mPDF 6\n    var $autoArabic; // mPDF 6\n\n    var $CJKforceend;\n    var $h2bookmarks;\n    var $h2toc;\n    var $decimal_align;\n    var $margBuffer;\n    var $splitTableBorderWidth;\n\n    var $bookmarkStyles;\n    var $useActiveForms;\n\n    var $repackageTTF;\n    var $allowCJKorphans;\n    var $allowCJKoverflow;\n\n    var $useKerning;\n    var $restrictColorSpace;\n    var $bleedMargin;\n    var $crossMarkMargin;\n    var $cropMarkMargin;\n    var $cropMarkLength;\n    var $nonPrintMargin;\n\n    var $PDFX;\n    var $PDFXauto;\n\n    var $PDFA;\n    var $PDFAversion = '1-B';\n    var $PDFAauto;\n    var $ICCProfile;\n\n    var $printers_info;\n    var $iterationCounter;\n    var $smCapsScale;\n    var $smCapsStretch;\n\n    var $backupSubsFont;\n    var $backupSIPFont;\n    var $fonttrans;\n    var $debugfonts;\n    var $useAdobeCJK;\n    var $percentSubset;\n    var $maxTTFFilesize;\n    var $BMPonly;\n\n    var $tableMinSizePriority;\n\n    var $dpi;\n    var $watermarkImgAlphaBlend;\n    var $watermarkImgBehind;\n    var $justifyB4br;\n    var $packTableData;\n    var $pgsIns;\n    var $simpleTables;\n    var $enableImports;\n\n    var $debug;\n\n    var $setAutoTopMargin;\n    var $setAutoBottomMargin;\n    var $autoMarginPadding;\n    var $collapseBlockMargins;\n    var $falseBoldWeight;\n    var $normalLineheight;\n    var $incrementFPR1;\n    var $incrementFPR2;\n    var $incrementFPR3;\n    var $incrementFPR4;\n\n    var $SHYlang;\n    var $SHYleftmin;\n    var $SHYrightmin;\n    var $SHYcharmin;\n    var $SHYcharmax;\n    var $SHYlanguages;\n\n    // PageNumber Conditional Text\n    var $pagenumPrefix;\n    var $pagenumSuffix;\n\n    var $nbpgPrefix;\n    var $nbpgSuffix;\n    var $showImageErrors;\n    var $allow_output_buffering;\n    var $autoPadding;\n    var $tabSpaces;\n    var $autoLangToFont;\n    var $watermarkTextAlpha;\n    var $watermarkImageAlpha;\n    var $watermark_size;\n    var $watermark_pos;\n    var $annotSize;\n    var $annotMargin;\n    var $annotOpacity;\n    var $title2annots;\n    var $keepColumns;\n    var $keep_table_proportions;\n    var $ignore_table_widths;\n    var $ignore_table_percents;\n    var $list_number_suffix;\n\n    var $list_auto_mode; // mPDF 6\n    var $list_indent_first_level; // mPDF 6\n    var $list_indent_default; // mPDF 6\n    var $list_indent_default_mpdf;\n    var $list_marker_offset; // mPDF 6\n    var $list_symbol_size;\n\n    var $useSubstitutions;\n    var $CSSselectMedia;\n\n    var $forcePortraitHeaders;\n    var $forcePortraitMargins;\n    var $displayDefaultOrientation;\n    var $ignore_invalid_utf8;\n    var $allowedCSStags;\n    var $onlyCoreFonts;\n    var $allow_charset_conversion;\n\n    var $jSWord;\n    var $jSmaxChar;\n    var $jSmaxCharLast;\n    var $jSmaxWordLast;\n\n    var $max_colH_correction;\n\n    var $table_error_report;\n    var $table_error_report_param;\n    var $biDirectional;\n    var $text_input_as_HTML;\n    var $anchor2Bookmark;\n    var $shrink_tables_to_fit;\n\n    var $allow_html_optional_endtags;\n\n    var $img_dpi;\n    var $whitelistStreamWrappers;\n\n    var $defaultheaderfontsize;\n    var $defaultheaderfontstyle;\n    var $defaultheaderline;\n    var $defaultfooterfontsize;\n    var $defaultfooterfontstyle;\n    var $defaultfooterline;\n    var $header_line_spacing;\n    var $footer_line_spacing;\n\n    var $pregCJKchars;\n    var $pregRTLchars;\n    var $pregCURSchars; // mPDF 6\n\n    var $mirrorMargins;\n    var $watermarkText;\n    var $watermarkAngle;\n    var $watermarkImage;\n    var $showWatermarkText;\n    var $showWatermarkImage;\n\n    var $svgAutoFont;\n    var $svgClasses;\n\n    var $fontsizes;\n\n    var $defaultPageNumStyle; // mPDF 6\n\n    //////////////////////\n    // INTERNAL VARIABLES\n    //////////////////////\n    var $extrapagebreak; // mPDF 6 pagebreaktype\n\n    var $uniqstr; // mPDF 5.7.2\n    var $hasOC;\n\n    var $textvar; // mPDF 5.7.1\n    var $fontLanguageOverride; // mPDF 5.7.1\n    var $OTLtags; // mPDF 5.7.1\n    var $OTLdata;  // mPDF 5.7.1\n\n    var $useDictionaryLBR;\n    var $useTibetanLBR;\n\n    var $writingToC;\n    var $layers;\n    var $layerDetails;\n    var $current_layer;\n    var $open_layer_pane;\n    var $decimal_offset;\n    var $inMeter;\n\n    var $CJKleading;\n    var $CJKfollowing;\n    var $CJKoverflow;\n\n    var $textshadow;\n\n    var $colsums;\n    var $spanborder;\n    var $spanborddet;\n\n    var $visibility;\n\n    var $kerning;\n    var $fixedlSpacing;\n    var $minwSpacing;\n    var $lSpacingCSS;\n    var $wSpacingCSS;\n\n    var $spotColorIDs;\n    var $SVGcolors;\n    var $spotColors;\n    var $defTextColor;\n    var $defDrawColor;\n    var $defFillColor;\n\n    var $tableBackgrounds;\n    var $inlineDisplayOff;\n    var $kt_y00;\n    var $kt_p00;\n    var $upperCase;\n    var $checkSIP;\n    var $checkSMP;\n    var $checkCJK;\n\n    var $watermarkImgAlpha;\n    var $PDFAXwarnings;\n\n    var $MetadataRoot;\n    var $OutputIntentRoot;\n    var $InfoRoot;\n    var $associatedFilesRoot;\n\n    var $pdf_version;\n\n    private $fontDir;\n\n    var $tempDir;\n\n    var $allowAnnotationFiles;\n\n    var $fontdata;\n\n    var $noImageFile;\n    var $lastblockbottommargin;\n    var $baselineC;\n\n    // mPDF 5.7.3  inline text-decoration parameters\n    var $baselineSup;\n    var $baselineSub;\n    var $baselineS;\n    var $baselineO;\n\n    var $subPos;\n    var $subArrMB;\n    var $ReqFontStyle;\n    var $tableClipPath;\n\n    var $fullImageHeight;\n\n    var $inFixedPosBlock;  // Internal flag for position:fixed block\n    var $fixedPosBlock;  // Buffer string for position:fixed block\n    var $fixedPosBlockDepth;\n    var $fixedPosBlockBBox;\n    var $fixedPosBlockSave;\n    var $maxPosL;\n    var $maxPosR;\n    var $loaded;\n\n    var $extraFontSubsets;\n\n    var $docTemplateStart;  // Internal flag for page (page no. -1) that docTemplate starts on\n\n    var $time0;\n\n    var $hyphenationDictionaryFile;\n\n    var $spanbgcolorarray;\n    var $default_font;\n    var $headerbuffer;\n    var $lastblocklevelchange;\n    var $nestedtablejustfinished;\n    var $linebreakjustfinished;\n    var $cell_border_dominance_L;\n    var $cell_border_dominance_R;\n    var $cell_border_dominance_T;\n    var $cell_border_dominance_B;\n    var $table_keep_together;\n    var $plainCell_properties;\n    var $shrin_k1;\n    var $outerfilled;\n\n    var $blockContext;\n    var $floatDivs;\n\n    var $patterns;\n    var $pageBackgrounds;\n\n    var $bodyBackgroundGradient;\n    var $bodyBackgroundImage;\n    var $bodyBackgroundColor;\n\n    var $writingHTMLheader; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block\n    var $writingHTMLfooter;\n\n    var $angle;\n\n    var $gradients;\n\n    var $kwt_Reference;\n    var $kwt_BMoutlines;\n    var $kwt_toc;\n\n    var $tbrot_BMoutlines;\n    var $tbrot_toc;\n\n    var $col_BMoutlines;\n    var $col_toc;\n\n    var $floatbuffer;\n    var $floatmargins;\n\n    var $bullet;\n    var $bulletarray;\n\n    var $currentLang;\n    var $default_lang;\n\n    var $default_available_fonts;\n\n    var $pageTemplate;\n    var $docTemplate;\n    var $docTemplateContinue;\n\n    var $arabGlyphs;\n    var $arabHex;\n    var $persianGlyphs;\n    var $persianHex;\n    var $arabVowels;\n    var $arabPrevLink;\n    var $arabNextLink;\n\n    var $formobjects; // array of Form Objects for WMF\n    var $InlineProperties;\n    var $InlineAnnots;\n    var $InlineBDF; // mPDF 6 Bidirectional formatting\n    var $InlineBDFctr; // mPDF 6\n\n    var $ktAnnots;\n    var $tbrot_Annots;\n    var $kwt_Annots;\n    var $columnAnnots;\n    var $columnForms;\n    var $tbrotForms;\n\n    var $PageAnnots;\n\n    var $pageDim; // Keep track of page wxh for orientation changes - set in _beginpage, used in _putannots\n\n    var $breakpoints;\n\n    var $tableLevel;\n    var $tbctr;\n    var $innermostTableLevel;\n    var $saveTableCounter;\n    var $cellBorderBuffer;\n\n    var $saveHTMLFooter_height;\n    var $saveHTMLFooterE_height;\n\n    var $firstPageBoxHeader;\n    var $firstPageBoxHeaderEven;\n    var $firstPageBoxFooter;\n    var $firstPageBoxFooterEven;\n\n    var $page_box;\n\n    var $show_marks; // crop or cross marks\n    var $basepathIsLocal;\n\n    var $use_kwt;\n    var $kwt;\n    var $kwt_height;\n    var $kwt_y0;\n    var $kwt_x0;\n    var $kwt_buffer;\n    var $kwt_Links;\n    var $kwt_moved;\n    var $kwt_saved;\n\n    var $PageNumSubstitutions;\n\n    var $table_borders_separate;\n    var $base_table_properties;\n    var $borderstyles;\n\n    var $blockjustfinished;\n\n    var $orig_bMargin;\n    var $orig_tMargin;\n    var $orig_lMargin;\n    var $orig_rMargin;\n    var $orig_hMargin;\n    var $orig_fMargin;\n\n    var $pageHTMLheaders;\n    var $pageHTMLfooters;\n\n    var $saveHTMLHeader;\n    var $saveHTMLFooter;\n\n    var $HTMLheaderPageLinks;\n    var $HTMLheaderPageAnnots;\n    var $HTMLheaderPageForms;\n\n    // See Config\\FontVariables for these next 5 values\n    var $available_unifonts;\n    var $sans_fonts;\n    var $serif_fonts;\n    var $mono_fonts;\n    var $defaultSubsFont;\n\n    // List of ALL available CJK fonts (incl. styles) (Adobe add-ons)  hw removed\n    var $available_CJK_fonts;\n\n    var $HTMLHeader;\n    var $HTMLFooter;\n    var $HTMLHeaderE;\n    var $HTMLFooterE;\n    var $bufferoutput;\n\n    // CJK fonts\n    var $Big5_widths;\n    var $GB_widths;\n    var $SJIS_widths;\n    var $UHC_widths;\n\n    // SetProtection\n    var $encrypted;\n\n    var $enc_obj_id; // encryption object id\n\n    // Bookmark\n    var $BMoutlines;\n    var $OutlineRoot;\n\n    // INDEX\n    var $ColActive;\n    var $Reference;\n    var $CurrCol;\n    var $NbCol;\n    var $y0;   // Top ordinate of columns\n\n    var $ColL;\n    var $ColWidth;\n    var $ColGap;\n\n    // COLUMNS\n    var $ColR;\n    var $ChangeColumn;\n    var $columnbuffer;\n    var $ColDetails;\n    var $columnLinks;\n    var $colvAlign;\n\n    // Substitutions\n    var $substitute;  // Array of substitution strings e.g. <ttz>112</ttz>\n    var $entsearch;  // Array of HTML entities (>ASCII 127) to substitute\n    var $entsubstitute; // Array of substitution decimal unicode for the Hi entities\n\n    // Default values if no style sheet offered\t(cf. http://www.w3.org/TR/CSS21/sample.html)\n    var $defaultCSS;\n    var $defaultCssFile;\n\n    var $lastoptionaltag; // Save current block item which HTML specifies optionsl endtag\n    var $pageoutput;\n    var $charset_in;\n    var $blk;\n    var $blklvl;\n    var $ColumnAdjust;\n\n    var $ws; // Word spacing\n\n    var $HREF;\n    var $pgwidth;\n    var $fontlist;\n    var $oldx;\n    var $oldy;\n    var $B;\n    var $I;\n\n    var $tdbegin;\n    var $table;\n    var $cell;\n    var $col;\n    var $row;\n\n    var $divbegin;\n    var $divwidth;\n    var $divheight;\n    var $spanbgcolor;\n\n    // mPDF 6 Used for table cell (block-type) properties\n    var $cellTextAlign;\n    var $cellLineHeight;\n    var $cellLineStackingStrategy;\n    var $cellLineStackingShift;\n\n    // mPDF 6  Lists\n    var $listcounter;\n    var $listlvl;\n    var $listtype;\n    var $listitem;\n\n    var $pjustfinished;\n    var $ignorefollowingspaces;\n    var $SMALL;\n    var $BIG;\n    var $dash_on;\n    var $dotted_on;\n\n    var $textbuffer;\n    var $currentfontstyle;\n    var $currentfontfamily;\n    var $currentfontsize;\n    var $colorarray;\n    var $bgcolorarray;\n    var $internallink;\n    var $enabledtags;\n\n    var $lineheight;\n    var $default_lineheight_correction;\n    var $basepath;\n    var $textparam;\n\n    var $specialcontent;\n    var $selectoption;\n    var $objectbuffer;\n\n    // Table Rotation\n    var $table_rotate;\n    var $tbrot_maxw;\n    var $tbrot_maxh;\n    var $tablebuffer;\n    var $tbrot_align;\n    var $tbrot_Links;\n\n    var $keep_block_together; // Keep a Block from page-break-inside: avoid\n\n    var $tbrot_y0;\n    var $tbrot_x0;\n    var $tbrot_w;\n    var $tbrot_h;\n\n    var $mb_enc;\n    var $originalMbEnc;\n    var $originalMbRegexEnc;\n\n    var $directionality;\n\n    var $extgstates; // Used for alpha channel - Transparency (Watermark)\n    var $mgl;\n    var $mgt;\n    var $mgr;\n    var $mgb;\n\n    var $tts;\n    var $ttz;\n    var $tta;\n\n    // Best to alter the below variables using default stylesheet above\n    var $page_break_after_avoid;\n    var $margin_bottom_collapse;\n    var $default_font_size; // in pts\n    var $original_default_font_size; // used to save default sizes when using table default\n    var $original_default_font;\n    var $watermark_font;\n    var $defaultAlign;\n\n    // TABLE\n    var $defaultTableAlign;\n    var $tablethead;\n    var $thead_font_weight;\n    var $thead_font_style;\n    var $thead_font_smCaps;\n    var $thead_valign_default;\n    var $thead_textalign_default;\n    var $tabletfoot;\n    var $tfoot_font_weight;\n    var $tfoot_font_style;\n    var $tfoot_font_smCaps;\n    var $tfoot_valign_default;\n    var $tfoot_textalign_default;\n\n    var $trow_text_rotate;\n\n    var $cellPaddingL;\n    var $cellPaddingR;\n    var $cellPaddingT;\n    var $cellPaddingB;\n    var $table_border_attr_set;\n    var $table_border_css_set;\n\n    var $shrin_k; // factor with which to shrink tables - used internally - do not change\n    var $shrink_this_table_to_fit; // 0 or false to disable; value (if set) gives maximum factor to reduce fontsize\n    var $MarginCorrection; // corrects for OddEven Margins\n    var $margin_footer;\n    var $margin_header;\n\n    var $tabletheadjustfinished;\n    var $usingCoreFont;\n    var $charspacing;\n\n    var $js;\n\n    /**\n     * Set timeout for cURL\n     *\n     * @var int\n     */\n    var $curlTimeout;\n\n    /**\n     * Set to true to follow redirects with cURL.\n     *\n     * @var bool\n     */\n    var $curlFollowLocation;\n\n    /**\n     * Set your own CA certificate store for SSL Certificate verification when using cURL\n     *\n     * Useful setting to use on hosts with outdated CA certificates.\n     *\n     * Download the latest CA certificate from https://curl.haxx.se/docs/caextract.html\n     *\n     * @var string The absolute path to the pem file\n     */\n    var $curlCaCertificate;\n\n    /**\n     * Set to true to allow unsafe SSL HTTPS requests.\n     *\n     * Can be useful when using CDN with HTTPS and if you don't want to configure settings with SSL certificates.\n     *\n     * @var bool\n     */\n    var $curlAllowUnsafeSslRequests;\n\n    /**\n     * Set the proxy for cURL.\n     *\n     * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html\n     *\n     * @var string\n     */\n    var $curlProxy;\n\n    /**\n     * Set the proxy auth for cURL.\n     *\n     * @see https://curl.haxx.se/libcurl/c/CURLOPT_PROXYUSERPWD.html\n     *\n     * @var string\n     */\n    var $curlProxyAuth;\n\n    // Private properties FROM FPDF\n    var $DisplayPreferences;\n    var $flowingBlockAttr;\n\n    var $page; // current page number\n\n    var $n; // current object number\n    var $n_js; // current object number\n\n    var $n_ocg_hidden;\n    var $n_ocg_print;\n    var $n_ocg_view;\n\n    var $offsets; // array of object offsets\n    var $buffer; // buffer holding in-memory PDF\n    var $pages; // array containing pages\n    var $state; // current document state\n    var $compress; // compression flag\n\n    var $DefOrientation; // default orientation\n    var $CurOrientation; // current orientation\n    var $OrientationChanges; // array indicating orientation changes\n\n    var $fwPt;\n    var $fhPt; // dimensions of page format in points\n    var $fw;\n    var $fh; // dimensions of page format in user unit\n    var $wPt;\n    var $hPt; // current dimensions of page in points\n\n    var $w;\n    var $h; // current dimensions of page in user unit\n\n    var $lMargin; // left margin\n    var $tMargin; // top margin\n    var $rMargin; // right margin\n    var $bMargin; // page break margin\n    var $cMarginL; // cell margin Left\n    var $cMarginR; // cell margin Right\n    var $cMarginT; // cell margin Left\n    var $cMarginB; // cell margin Right\n\n    var $DeflMargin; // Default left margin\n    var $DefrMargin; // Default right margin\n\n    var $x;\n    var $y; // current position in user unit for cell positioning\n\n    var $lasth; // height of last cell printed\n    var $LineWidth; // line width in user unit\n\n    var $CoreFonts; // array of standard font names\n    var $fonts; // array of used fonts\n    var $FontFiles; // array of font files\n\n    var $images; // array of used images\n    var $imageVars = []; // array of image vars\n\n    var $PageLinks; // array of links in pages\n    var $links; // array of internal links\n    var $FontFamily; // current font family\n    var $FontStyle; // current font style\n    var $CurrentFont; // current font info\n    var $FontSizePt; // current font size in points\n    var $FontSize; // current font size in user unit\n    var $DrawColor; // commands for drawing color\n    var $FillColor; // commands for filling color\n    var $TextColor; // commands for text color\n    var $ColorFlag; // indicates whether fill and text colors are different\n    var $autoPageBreak; // automatic page breaking\n    var $PageBreakTrigger; // threshold used to trigger page breaks\n    var $InFooter; // flag set when processing footer\n\n    var $InHTMLFooter;\n    var $processingFooter; // flag set when processing footer - added for columns\n    var $processingHeader; // flag set when processing header - added for columns\n    var $ZoomMode; // zoom display mode\n    var $LayoutMode; // layout display mode\n    var $title; // title\n    var $subject; // subject\n    var $author; // author\n    var $keywords; // keywords\n    var $creator; // creator\n\n    var $customProperties; // array of custom document properties\n\n    var $associatedFiles; // associated files (see SetAssociatedFiles below)\n    var $additionalXmpRdf; // additional rdf added in xmp\n\n    var $aliasNbPg; // alias for total number of pages\n    var $aliasNbPgGp; // alias for total number of pages in page group\n\n    var $ispre;\n    var $outerblocktags;\n    var $innerblocktags;\n\n    public $exposeVersion;\n\n    /**\n     * @var string\n     */\n    private $fontDescriptor;\n\n    /**\n     * @var \\Mpdf\\Otl\n     */\n    private $otl;\n\n    /**\n     * @var \\Mpdf\\CssManager\n     */\n    private $cssManager;\n\n    /**\n     * @var \\Mpdf\\Gradient\n     */\n    private $gradient;\n\n    /**\n     * @var \\Mpdf\\Image\\Bmp\n     */\n    private $bmp;\n\n    /**\n     * @var \\Mpdf\\Image\\Wmf\n     */\n    private $wmf;\n\n    /**\n     * @var \\Mpdf\\TableOfContents\n     */\n    private $tableOfContents;\n\n    /**\n     * @var \\Mpdf\\Form\n     */\n    private $form;\n\n    /**\n     * @var \\Mpdf\\DirectWrite\n     */\n    private $directWrite;\n\n    /**\n     * @var \\Mpdf\\Cache\n     */\n    private $cache;\n\n    /**\n     * @var \\Mpdf\\Fonts\\FontCache\n     */\n    private $fontCache;\n\n    /**\n     * @var \\Mpdf\\Fonts\\FontFileFinder\n     */\n    private $fontFileFinder;\n\n    /**\n     * @var \\Mpdf\\Tag\n     */\n    private $tag;\n\n    /**\n     * @var \\Mpdf\\Barcode\n     * @todo solve Tag dependency and make private\n     */\n    public $barcode;\n\n    /**\n     * @var \\Mpdf\\QrCode\\QrCode\n     */\n    private $qrcode;\n\n    /**\n     * @var \\Mpdf\\SizeConverter\n     */\n    private $sizeConverter;\n\n    /**\n     * @var \\Mpdf\\Color\\ColorConverter\n     */\n    private $colorConverter;\n\n    /**\n     * @var \\Mpdf\\Color\\ColorModeConverter\n     */\n    private $colorModeConverter;\n\n    /**\n     * @var \\Mpdf\\Color\\ColorSpaceRestrictor\n     */\n    private $colorSpaceRestrictor;\n\n    /**\n     * @var \\Mpdf\\Hyphenator\n     */\n    private $hyphenator;\n\n    /**\n     * @var \\Mpdf\\Pdf\\Protection\n     */\n    private $protection;\n\n    /**\n     * @var \\Mpdf\\RemoteContentFetcher\n     */\n    private $remoteContentFetcher;\n\n    /**\n     * @var \\Mpdf\\Image\\ImageProcessor\n     */\n    private $imageProcessor;\n\n    /**\n     * @var \\Mpdf\\Language\\LanguageToFontInterface\n     */\n    private $languageToFont;\n\n    /**\n     * @var \\Mpdf\\Language\\ScriptToLanguageInterface\n     */\n    private $scriptToLanguage;\n\n    /**\n     * @var \\Psr\\Log\\LoggerInterface\n     */\n    private $logger;\n\n    /**\n     * @var \\Mpdf\\Writer\\BaseWriter\n     */\n    private $writer;\n\n    /**\n     * @var \\Mpdf\\Writer\\FontWriter\n     */\n    private $fontWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\MetadataWriter\n     */\n    private $metadataWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\ImageWriter\n     */\n    private $imageWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\FormWriter\n     */\n    private $formWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\PageWriter\n     */\n    private $pageWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\BookmarkWriter\n     */\n    private $bookmarkWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\OptionalContentWriter\n     */\n    private $optionalContentWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\ColorWriter\n     */\n    private $colorWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\BackgroundWriter\n     */\n    private $backgroundWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\JavaScriptWriter\n     */\n    private $javaScriptWriter;\n\n    /**\n     * @var \\Mpdf\\Writer\\ResourceWriter\n     */\n    private $resourceWriter;\n\n    /**\n     * @var string[]\n     */\n    private $services;\n\n    /**\n     * @param mixed[] $config\n     */\n    public function __construct(array $config = [])\n    {\n        $this->_dochecks();\n\n        [\n            $mode,\n            $format,\n            $default_font_size,\n            $default_font,\n            $mgl,\n            $mgr,\n            $mgt,\n            $mgb,\n            $mgh,\n            $mgf,\n            $orientation\n            ] = $this->initConstructorParams($config);\n\n        $this->logger = new NullLogger();\n\n        $originalConfig = $config;\n        $config = $this->initConfig($originalConfig);\n\n        $serviceFactory = new ServiceFactory();\n        $services = $serviceFactory->getServices(\n            $this,\n            $this->logger,\n            $config,\n            $this->restrictColorSpace,\n            $this->languageToFont,\n            $this->scriptToLanguage,\n            $this->fontDescriptor,\n            $this->bmp,\n            $this->directWrite,\n            $this->wmf\n        );\n\n        $this->services = [];\n\n        foreach ($services as $key => $service) {\n            $this->{$key} = $service;\n            $this->services[] = $key;\n        }\n\n        $this->time0 = microtime(true);\n\n        $this->writingToC = false;\n\n        $this->layers = [];\n        $this->current_layer = 0;\n        $this->open_layer_pane = false;\n\n        $this->visibility = 'visible';\n\n        $this->tableBackgrounds = [];\n        $this->uniqstr = '20110230'; // mPDF 5.7.2\n        $this->kt_y00 = 0;\n        $this->kt_p00 = 0;\n        $this->BMPonly = [];\n        $this->page = 0;\n        $this->n = 2;\n        $this->buffer = '';\n        $this->objectbuffer = [];\n        $this->pages = [];\n        $this->OrientationChanges = [];\n        $this->state = 0;\n        $this->fonts = [];\n        $this->FontFiles = [];\n        $this->images = [];\n        $this->links = [];\n        $this->InFooter = false;\n        $this->processingFooter = false;\n        $this->processingHeader = false;\n        $this->lasth = 0;\n        $this->FontFamily = '';\n        $this->FontStyle = '';\n        $this->FontSizePt = 9;\n\n        // Small Caps\n        $this->inMeter = false;\n        $this->decimal_offset = 0;\n\n        $this->PDFAXwarnings = [];\n\n        $this->defTextColor = $this->TextColor = $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);\n        $this->defDrawColor = $this->DrawColor = $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings), true);\n        $this->defFillColor = $this->FillColor = $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings), true);\n\n        $this->upperCase = require __DIR__ . '/../data/upperCase.php';\n\n        $this->extrapagebreak = true; // mPDF 6 pagebreaktype\n\n        $this->ColorFlag = false;\n        $this->extgstates = [];\n\n        $this->mb_enc = 'windows-1252';\n        $this->originalMbEnc = mb_internal_encoding();\n        $this->originalMbRegexEnc = mb_regex_encoding();\n\n        $this->directionality = 'ltr';\n        $this->defaultAlign = 'L';\n        $this->defaultTableAlign = 'L';\n\n        $this->fixedPosBlockSave = [];\n        $this->extraFontSubsets = 0;\n\n        $this->blockContext = 1;\n        $this->floatDivs = [];\n        $this->DisplayPreferences = '';\n\n        // Tiling patterns used for backgrounds\n        $this->patterns = [];\n        $this->pageBackgrounds = [];\n        $this->gradients = [];\n\n        // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block\n        $this->writingHTMLheader = false;\n        // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block\n        $this->writingHTMLfooter = false;\n\n        $this->kwt_Reference = [];\n        $this->kwt_BMoutlines = [];\n        $this->kwt_toc = [];\n\n        $this->tbrot_BMoutlines = [];\n        $this->tbrot_toc = [];\n\n        $this->col_BMoutlines = [];\n        $this->col_toc = [];\n\n        $this->pgsIns = [];\n        $this->PDFAXwarnings = [];\n        $this->inlineDisplayOff = false;\n        $this->lSpacingCSS = '';\n        $this->wSpacingCSS = '';\n        $this->fixedlSpacing = false;\n        $this->minwSpacing = 0;\n\n        // Baseline for text\n        $this->baselineC = 0.35;\n\n        // mPDF 5.7.3  inline text-decoration parameters\n        // Sets default change in baseline for <sup> text as factor of preceeding fontsize\n        // 0.35 has been recommended; 0.5 matches applications like MS Word\n        $this->baselineSup = 0.5;\n\n        // Sets default change in baseline for <sub> text as factor of preceeding fontsize\n        $this->baselineSub = -0.2;\n        // Sets default height for <strike> text as factor of fontsize\n        $this->baselineS = 0.3;\n        // Sets default height for overline text as factor of fontsize\n        $this->baselineO = 1.1;\n\n        $this->noImageFile = __DIR__ . '/../data/no_image.jpg';\n        $this->subPos = 0;\n\n        $this->fullImageHeight = false;\n        $this->floatbuffer = [];\n        $this->floatmargins = [];\n        $this->formobjects = []; // array of Form Objects for WMF\n        $this->InlineProperties = [];\n        $this->InlineAnnots = [];\n        $this->InlineBDF = []; // mPDF 6\n        $this->InlineBDFctr = 0; // mPDF 6\n        $this->tbrot_Annots = [];\n        $this->kwt_Annots = [];\n        $this->columnAnnots = [];\n        $this->PageLinks = [];\n        $this->OrientationChanges = [];\n        $this->pageDim = [];\n        $this->saveHTMLHeader = [];\n        $this->saveHTMLFooter = [];\n        $this->PageAnnots = [];\n        $this->PageNumSubstitutions = [];\n        $this->breakpoints = []; // used in columnbuffer\n        $this->tableLevel = 0;\n        $this->tbctr = []; // counter for nested tables at each level\n        $this->page_box = [];\n        $this->show_marks = ''; // crop or cross marks\n        $this->kwt = false;\n        $this->kwt_height = 0;\n        $this->kwt_y0 = 0;\n        $this->kwt_x0 = 0;\n        $this->kwt_buffer = [];\n        $this->kwt_Links = [];\n        $this->kwt_moved = false;\n        $this->kwt_saved = false;\n        $this->PageNumSubstitutions = [];\n        $this->base_table_properties = [];\n        $this->borderstyles = ['inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double'];\n        $this->tbrot_align = 'C';\n\n        $this->pageHTMLheaders = [];\n        $this->pageHTMLfooters = [];\n        $this->HTMLheaderPageLinks = [];\n        $this->HTMLheaderPageAnnots = [];\n\n        $this->HTMLheaderPageForms = [];\n        $this->columnForms = [];\n        $this->tbrotForms = [];\n\n        $this->pageoutput = [];\n\n        $this->bufferoutput = false;\n\n        $this->encrypted = false;\n\n        $this->BMoutlines = [];\n        $this->ColActive = 0;          // Flag indicating that columns are on (the index is being processed)\n        $this->Reference = [];    // Array containing the references\n        $this->CurrCol = 0;               // Current column number\n        $this->ColL = [0];   // Array of Left pos of columns - absolute - needs Margin correction for Odd-Even\n        $this->ColR = [0];   // Array of Right pos of columns - absolute pos - needs Margin correction for Odd-Even\n        $this->ChangeColumn = 0;\n        $this->columnbuffer = [];\n        $this->ColDetails = [];  // Keeps track of some column details\n        $this->columnLinks = [];  // Cross references PageLinks\n        $this->substitute = [];  // Array of substitution strings e.g. <ttz>112</ttz>\n        $this->entsearch = [];  // Array of HTML entities (>ASCII 127) to substitute\n        $this->entsubstitute = []; // Array of substitution decimal unicode for the Hi entities\n        $this->lastoptionaltag = '';\n        $this->charset_in = '';\n        $this->blk = [];\n        $this->blklvl = 0;\n        $this->tts = false;\n        $this->ttz = false;\n        $this->tta = false;\n        $this->ispre = false;\n\n        $this->checkSIP = false;\n        $this->checkSMP = false;\n        $this->checkCJK = false;\n\n        $this->page_break_after_avoid = false;\n        $this->margin_bottom_collapse = false;\n        $this->tablethead = 0;\n        $this->tabletfoot = 0;\n        $this->table_border_attr_set = 0;\n        $this->table_border_css_set = 0;\n        $this->shrin_k = 1.0;\n        $this->shrink_this_table_to_fit = 0;\n        $this->MarginCorrection = 0;\n\n        $this->tabletheadjustfinished = false;\n        $this->usingCoreFont = false;\n        $this->charspacing = 0;\n\n        $this->autoPageBreak = true;\n\n        $this->_setPageSize($format, $orientation);\n        $this->DefOrientation = $orientation;\n\n        $this->margin_header = $mgh;\n        $this->margin_footer = $mgf;\n\n        $bmargin = $mgb;\n\n        $this->DeflMargin = $mgl;\n        $this->DefrMargin = $mgr;\n\n        $this->orig_tMargin = $mgt;\n        $this->orig_bMargin = $bmargin;\n        $this->orig_lMargin = $this->DeflMargin;\n        $this->orig_rMargin = $this->DefrMargin;\n        $this->orig_hMargin = $this->margin_header;\n        $this->orig_fMargin = $this->margin_footer;\n\n        if ($this->setAutoTopMargin == 'pad') {\n            $mgt += $this->margin_header;\n        }\n        if ($this->setAutoBottomMargin == 'pad') {\n            $mgb += $this->margin_footer;\n        }\n\n        // sets l r t margin\n        $this->SetMargins($this->DeflMargin, $this->DefrMargin, $mgt);\n\n        // Automatic page break\n        // sets $this->bMargin & PageBreakTrigger\n        $this->SetAutoPageBreak($this->autoPageBreak, $bmargin);\n\n        $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n\n        // Interior cell margin (1 mm) ? not used\n        $this->cMarginL = 1;\n        $this->cMarginR = 1;\n\n        // Line width (0.2 mm)\n        $this->LineWidth = .567 / Mpdf::SCALE;\n\n        // Enable all tags as default\n        $this->DisableTags();\n        // Full width display mode\n        $this->SetDisplayMode(100); // fullwidth? 'fullpage'\n\n        // Compression\n        $this->SetCompression(true);\n        // Set default display preferences\n        $this->SetDisplayPreferences('');\n\n        $this->initFontConfig($originalConfig);\n\n        // Available fonts\n        $this->available_unifonts = [];\n        foreach ($this->fontdata as $f => $fs) {\n            if (isset($fs['R']) && $fs['R']) {\n                $this->available_unifonts[] = $f;\n            }\n            if (isset($fs['B']) && $fs['B']) {\n                $this->available_unifonts[] = $f . 'B';\n            }\n            if (isset($fs['I']) && $fs['I']) {\n                $this->available_unifonts[] = $f . 'I';\n            }\n            if (isset($fs['BI']) && $fs['BI']) {\n                $this->available_unifonts[] = $f . 'BI';\n            }\n        }\n\n        $this->default_available_fonts = $this->available_unifonts;\n\n        $optcore = false;\n        $onlyCoreFonts = false;\n        if (preg_match('/([\\-+])aCJK/i', $mode, $m)) {\n            $mode = preg_replace('/([\\-+])aCJK/i', '', $mode); // mPDF 6\n            if ($m[1] == '+') {\n                $this->useAdobeCJK = true;\n            } else {\n                $this->useAdobeCJK = false;\n            }\n        }\n\n        if (strlen($mode) == 1) {\n            if ($mode == 's') {\n                $this->percentSubset = 100;\n                $mode = '';\n            } elseif ($mode == 'c') {\n                $onlyCoreFonts = true;\n                $mode = '';\n            }\n        } elseif (substr($mode, -2) == '-s') {\n            $this->percentSubset = 100;\n            $mode = substr($mode, 0, strlen($mode) - 2);\n        } elseif (substr($mode, -2) == '-c') {\n            $onlyCoreFonts = true;\n            $mode = substr($mode, 0, strlen($mode) - 2);\n        } elseif (substr($mode, -2) == '-x') {\n            $optcore = true;\n            $mode = substr($mode, 0, strlen($mode) - 2);\n        }\n\n        // Autodetect if mode is a language_country string (en-GB or en_GB or en)\n        if ($mode && $mode != 'UTF-8') { // mPDF 6\n            [$coreSuitable, $mpdf_pdf_unifont] = $this->languageToFont->getLanguageOptions($mode, $this->useAdobeCJK);\n            if ($coreSuitable && $optcore) {\n                $onlyCoreFonts = true;\n            }\n            if ($mpdf_pdf_unifont) {  // mPDF 6\n                $default_font = $mpdf_pdf_unifont;\n            }\n            $this->currentLang = $mode;\n            $this->default_lang = $mode;\n        }\n\n        $this->onlyCoreFonts = $onlyCoreFonts;\n\n        if ($this->onlyCoreFonts) {\n            $this->setMBencoding('windows-1252'); // sets $this->mb_enc\n        } else {\n            $this->setMBencoding('UTF-8'); // sets $this->mb_enc\n        }\n        @mb_regex_encoding('UTF-8'); // required only for mb_ereg... and mb_split functions\n\n        // Adobe CJK fonts\n        $this->available_CJK_fonts = [\n            'gb',\n            'big5',\n            'sjis',\n            'uhc',\n            'gbB',\n            'big5B',\n            'sjisB',\n            'uhcB',\n            'gbI',\n            'big5I',\n            'sjisI',\n            'uhcI',\n            'gbBI',\n            'big5BI',\n            'sjisBI',\n            'uhcBI',\n        ];\n\n        // Standard fonts\n        $this->CoreFonts = [\n            'ccourier' => 'Courier',\n            'ccourierB' => 'Courier-Bold',\n            'ccourierI' => 'Courier-Oblique',\n            'ccourierBI' => 'Courier-BoldOblique',\n            'chelvetica' => 'Helvetica',\n            'chelveticaB' => 'Helvetica-Bold',\n            'chelveticaI' => 'Helvetica-Oblique',\n            'chelveticaBI' => 'Helvetica-BoldOblique',\n            'ctimes' => 'Times-Roman',\n            'ctimesB' => 'Times-Bold',\n            'ctimesI' => 'Times-Italic',\n            'ctimesBI' => 'Times-BoldItalic',\n            'csymbol' => 'Symbol',\n            'czapfdingbats' => 'ZapfDingbats'\n        ];\n\n        $this->fontlist = [\n            \"ctimes\",\n            \"ccourier\",\n            \"chelvetica\",\n            \"csymbol\",\n            \"czapfdingbats\"\n        ];\n\n        // Substitutions\n        $this->setHiEntitySubstitutions();\n\n        if ($this->onlyCoreFonts) {\n            $this->useSubstitutions = true;\n            $this->SetSubstitutions();\n        } else {\n            $this->useSubstitutions = $config['useSubstitutions'];\n        }\n\n        if (file_exists($this->defaultCssFile)) {\n            $css = file_get_contents($this->defaultCssFile);\n            $this->cssManager->ReadCSS('<style> ' . $css . ' </style>');\n        } else {\n            throw new \\Mpdf\\MpdfException(sprintf('Unable to read default CSS file \"%s\"', $this->defaultCssFile));\n        }\n\n        if ($default_font == '') {\n            if ($this->onlyCoreFonts) {\n                if (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->mono_fonts)) {\n                    $default_font = 'ccourier';\n                } elseif (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->sans_fonts)) {\n                    $default_font = 'chelvetica';\n                } else {\n                    $default_font = 'ctimes';\n                }\n            } else {\n                $default_font = $this->defaultCSS['BODY']['FONT-FAMILY'];\n            }\n        }\n        if (!$default_font_size) {\n            $mmsize = $this->sizeConverter->convert($this->defaultCSS['BODY']['FONT-SIZE']);\n            $default_font_size = $mmsize * (Mpdf::SCALE);\n        }\n\n        if ($default_font) {\n            $this->SetDefaultFont($default_font);\n        }\n        if ($default_font_size) {\n            $this->SetDefaultFontSize($default_font_size);\n        }\n\n        $this->SetLineHeight(); // lineheight is in mm\n\n        $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n        $this->HREF = '';\n        $this->oldy = -1;\n        $this->B = 0;\n        $this->I = 0;\n\n        // mPDF 6  Lists\n        $this->listlvl = 0;\n        $this->listtype = [];\n        $this->listitem = [];\n        $this->listcounter = [];\n\n        $this->tdbegin = false;\n        $this->table = [];\n        $this->cell = [];\n        $this->col = -1;\n        $this->row = -1;\n        $this->cellBorderBuffer = [];\n\n        $this->divbegin = false;\n        // mPDF 6\n        $this->cellTextAlign = '';\n        $this->cellLineHeight = '';\n        $this->cellLineStackingStrategy = '';\n        $this->cellLineStackingShift = '';\n\n        $this->divwidth = 0;\n        $this->divheight = 0;\n        $this->spanbgcolor = false;\n        $this->spanborder = false;\n        $this->spanborddet = [];\n\n        $this->blockjustfinished = false;\n        $this->ignorefollowingspaces = true; // in order to eliminate exceeding left-side spaces\n        $this->dash_on = false;\n        $this->dotted_on = false;\n        $this->textshadow = '';\n\n        $this->currentfontfamily = '';\n        $this->currentfontsize = '';\n        $this->currentfontstyle = '';\n        $this->colorarray = ''; // mPDF 6\n        $this->spanbgcolorarray = ''; // mPDF 6\n        $this->textbuffer = [];\n        $this->internallink = [];\n        $this->basepath = \"\";\n\n        $this->SetBasePath('');\n\n        $this->textparam = [];\n\n        $this->specialcontent = '';\n        $this->selectoption = [];\n    }\n\n    public function cleanup()\n    {\n        mb_internal_encoding($this->originalMbEnc);\n        @mb_regex_encoding($this->originalMbRegexEnc);\n    }\n\n    /**\n     * @param \\Psr\\Log\\LoggerInterface\n     *\n     * @return \\Mpdf\\Mpdf\n     */\n    public function setLogger(LoggerInterface $logger)\n    {\n        $this->logger = $logger;\n\n        foreach ($this->services as $name) {\n            if ($this->$name && $this->$name instanceof \\Psr\\Log\\LoggerAwareInterface) {\n                $this->$name->setLogger($logger);\n            }\n        }\n\n        return $this;\n    }\n\n    private function initConfig(array $config)\n    {\n        $configObject = new ConfigVariables();\n        $defaults = $configObject->getDefaults();\n        $config = array_intersect_key($config + $defaults, $defaults);\n\n        foreach ($config as $var => $val) {\n            $this->{$var} = $val;\n        }\n\n        return $config;\n    }\n\n    private function initConstructorParams(array $config)\n    {\n        $constructor = [\n            'mode' => '',\n            'format' => 'A4',\n            'default_font_size' => 0,\n            'default_font' => '',\n            'margin_left' => 15,\n            'margin_right' => 15,\n            'margin_top' => 16,\n            'margin_bottom' => 16,\n            'margin_header' => 9,\n            'margin_footer' => 9,\n            'orientation' => 'P',\n        ];\n\n        foreach ($constructor as $key => $val) {\n            if (isset($config[$key])) {\n                $constructor[$key] = $config[$key];\n            }\n        }\n\n        return array_values($constructor);\n    }\n\n    private function initFontConfig(array $config)\n    {\n        $configObject = new FontVariables();\n        $defaults = $configObject->getDefaults();\n        $config = array_intersect_key($config + $defaults, $defaults);\n        foreach ($config as $var => $val) {\n            $this->{$var} = $val;\n        }\n\n        return $config;\n    }\n\n    function _setPageSize($format, &$orientation)\n    {\n        if (is_string($format)) {\n\n            if (empty($format)) {\n                $format = 'A4';\n            }\n\n            // e.g. A4-L = A4 landscape, A4-P = A4 portrait\n            if (preg_match('/([0-9a-zA-Z]*)-([P,L])/i', $format, $m)) {\n                $format = $m[1];\n                $orientation = $m[2];\n            } elseif (empty($orientation)) {\n                $orientation = 'P';\n            }\n\n            $format = PageFormat::getSizeFromName($format);\n\n            $this->fwPt = $format[0];\n            $this->fhPt = $format[1];\n\n        } else {\n\n            if (!$format[0] || !$format[1]) {\n                throw new \\Mpdf\\MpdfException('Invalid page format: ' . $format[0] . ' ' . $format[1]);\n            }\n\n            $this->fwPt = $format[0] * Mpdf::SCALE;\n            $this->fhPt = $format[1] * Mpdf::SCALE;\n        }\n\n        $this->fw = $this->fwPt / Mpdf::SCALE;\n        $this->fh = $this->fhPt / Mpdf::SCALE;\n\n        // Page orientation\n        $orientation = strtolower($orientation);\n        if ($orientation === 'p' || $orientation == 'portrait') {\n            $orientation = 'P';\n            $this->wPt = $this->fwPt;\n            $this->hPt = $this->fhPt;\n        } elseif ($orientation === 'l' || $orientation == 'landscape') {\n            $orientation = 'L';\n            $this->wPt = $this->fhPt;\n            $this->hPt = $this->fwPt;\n        } else {\n            throw new \\Mpdf\\MpdfException('Incorrect orientation: ' . $orientation);\n        }\n\n        $this->CurOrientation = $orientation;\n\n        $this->w = $this->wPt / Mpdf::SCALE;\n        $this->h = $this->hPt / Mpdf::SCALE;\n    }\n\n    function RestrictUnicodeFonts($res)\n    {\n        // $res = array of (Unicode) fonts to restrict to: e.g. norasi|norasiB - language specific\n        if (count($res)) { // Leave full list of available fonts if passed blank array\n            $this->available_unifonts = $res;\n        } else {\n            $this->available_unifonts = $this->default_available_fonts;\n        }\n        if (count($this->available_unifonts) == 0) {\n            $this->available_unifonts[] = $this->default_available_fonts[0];\n        }\n        $this->available_unifonts = array_values($this->available_unifonts);\n    }\n\n    function setMBencoding($enc)\n    {\n        if ($this->mb_enc != $enc) {\n            $this->mb_enc = $enc;\n            mb_internal_encoding($this->mb_enc);\n        }\n    }\n\n    function SetMargins($left, $right, $top)\n    {\n        // Set left, top and right margins\n        $this->lMargin = $left;\n        $this->rMargin = $right;\n        $this->tMargin = $top;\n    }\n\n    function ResetMargins()\n    {\n        // ReSet left, top margins\n        if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P' && $this->CurOrientation == 'L') {\n            if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN\n                $this->tMargin = $this->orig_rMargin;\n                $this->bMargin = $this->orig_lMargin;\n            } else { // ODD\t// OR NOT MIRRORING MARGINS/FOOTERS\n                $this->tMargin = $this->orig_lMargin;\n                $this->bMargin = $this->orig_rMargin;\n            }\n            $this->lMargin = $this->DeflMargin;\n            $this->rMargin = $this->DefrMargin;\n            $this->MarginCorrection = 0;\n            $this->PageBreakTrigger = $this->h - $this->bMargin;\n        } elseif (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN\n            $this->lMargin = $this->DefrMargin;\n            $this->rMargin = $this->DeflMargin;\n            $this->MarginCorrection = $this->DefrMargin - $this->DeflMargin;\n        } else { // ODD\t// OR NOT MIRRORING MARGINS/FOOTERS\n            $this->lMargin = $this->DeflMargin;\n            $this->rMargin = $this->DefrMargin;\n            if ($this->mirrorMargins) {\n                $this->MarginCorrection = $this->DeflMargin - $this->DefrMargin;\n            }\n        }\n        $this->x = $this->lMargin;\n    }\n\n    function SetLeftMargin($margin)\n    {\n        // Set left margin\n        $this->lMargin = $margin;\n        if ($this->page > 0 and $this->x < $margin) {\n            $this->x = $margin;\n        }\n    }\n\n    function SetTopMargin($margin)\n    {\n        // Set top margin\n        $this->tMargin = $margin;\n    }\n\n    function SetRightMargin($margin)\n    {\n        // Set right margin\n        $this->rMargin = $margin;\n    }\n\n    function SetAutoPageBreak($auto, $margin = 0)\n    {\n        // Set auto page break mode and triggering margin\n        $this->autoPageBreak = $auto;\n        $this->bMargin = $margin;\n        $this->PageBreakTrigger = $this->h - $margin;\n    }\n\n    function SetDisplayMode($zoom, $layout = 'continuous')\n    {\n        $allowedZoomModes = ['fullpage', 'fullwidth', 'real', 'default', 'none'];\n\n        if (in_array($zoom, $allowedZoomModes, true) || is_numeric($zoom)) {\n            $this->ZoomMode = $zoom;\n        } else {\n            throw new \\Mpdf\\MpdfException('Incorrect zoom display mode: ' . $zoom);\n        }\n\n        $allowedLayoutModes = ['single', 'continuous', 'two', 'twoleft', 'tworight', 'default'];\n\n        if (in_array($layout, $allowedLayoutModes, true)) {\n            $this->LayoutMode = $layout;\n        } else {\n            throw new \\Mpdf\\MpdfException('Incorrect layout display mode: ' . $layout);\n        }\n    }\n\n    function SetCompression($compress)\n    {\n        // Set page compression\n        if (function_exists('gzcompress')) {\n            $this->compress = $compress;\n        } else {\n            $this->compress = false;\n        }\n    }\n\n    function SetTitle($title)\n    {\n        // Title of document // Arrives as UTF-8\n        $this->title = $title;\n    }\n\n    function SetSubject($subject)\n    {\n        // Subject of document\n        $this->subject = $subject;\n    }\n\n    function SetAuthor($author)\n    {\n        // Author of document\n        $this->author = $author;\n    }\n\n    function SetKeywords($keywords)\n    {\n        // Keywords of document\n        $this->keywords = $keywords;\n    }\n\n    function SetCreator($creator)\n    {\n        // Creator of document\n        $this->creator = $creator;\n    }\n\n    function AddCustomProperty($key, $value)\n    {\n        $this->customProperties[$key] = $value;\n    }\n\n    /**\n     * Set one or multiple associated file (\"/AF\" as required by PDF/A-3)\n     *\n     * param $files is an array of hash containing:\n     *   path: file path on FS\n     *   content: file content\n     *   name: file name (not necessarily the same as the file on FS)\n     *   mime (optional): file mime type (will show up as /Subtype in the PDF)\n     *   description (optional): file description\n     *   AFRelationship (optional): PDF/A-3 AFRelationship (e.g. \"Alternative\")\n     *\n     * e.g. to associate 1 file:\n     *     [[\n     *         'path' => 'tmp/1234.xml',\n     *         'content' => 'file content',\n     *         'name' => 'public_name.xml',\n     *         'mime' => 'text/xml',\n     *         'description' => 'foo',\n     *         'AFRelationship' => 'Alternative',\n     *     ]]\n     *\n     * @param mixed[] $files Array of arrays of associated files. See above\n     */\n    function SetAssociatedFiles(array $files)\n    {\n        $this->associatedFiles = $files;\n    }\n\n    function SetAdditionalXmpRdf($s)\n    {\n        $this->additionalXmpRdf = $s;\n    }\n\n    function SetAnchor2Bookmark($x)\n    {\n        $this->anchor2Bookmark = $x;\n    }\n\n    public function AliasNbPages($alias = '{nb}')\n    {\n        // Define an alias for total number of pages\n        $this->aliasNbPg = $alias;\n    }\n\n    public function AliasNbPageGroups($alias = '{nbpg}')\n    {\n        // Define an alias for total number of pages in a group\n        $this->aliasNbPgGp = $alias;\n    }\n\n    function SetAlpha($alpha, $bm = 'Normal', $return = false, $mode = 'B')\n    {\n        // alpha: real value from 0 (transparent) to 1 (opaque)\n        // bm:    blend mode, one of the following:\n        //          Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn,\n        //          HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity\n        // set alpha for stroking (CA) and non-stroking (ca) operations\n        // mode determines F (fill) S (stroke) B (both)\n        if (($this->PDFA || $this->PDFX) && $alpha != 1) {\n            if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {\n                $this->PDFAXwarnings[] = \"Image opacity must be 100% (Opacity changed to 100%)\";\n            }\n            $alpha = 1;\n        }\n        $a = ['BM' => '/' . $bm];\n        if ($mode == 'F' || $mode == 'B') {\n            $a['ca'] = $alpha; // mPDF 5.7.2\n        }\n        if ($mode == 'S' || $mode == 'B') {\n            $a['CA'] = $alpha; // mPDF 5.7.2\n        }\n        $gs = $this->AddExtGState($a);\n        if ($return) {\n            return sprintf('/GS%d gs', $gs);\n        } else {\n            $this->writer->write(sprintf('/GS%d gs', $gs));\n        }\n    }\n\n    function AddExtGState($parms)\n    {\n        $n = count($this->extgstates);\n        // check if graphics state already exists\n        for ($i = 1; $i <= $n; $i++) {\n            if (count($this->extgstates[$i]['parms']) == count($parms)) {\n                $same = true;\n                foreach ($this->extgstates[$i]['parms'] as $k => $v) {\n                    if (!isset($parms[$k]) || $parms[$k] != $v) {\n                        $same = false;\n                        break;\n                    }\n                }\n                if ($same) {\n                    return $i;\n                }\n            }\n        }\n        $n++;\n        $this->extgstates[$n]['parms'] = $parms;\n        return $n;\n    }\n\n    function SetVisibility($v)\n    {\n        if (($this->PDFA || $this->PDFX) && $this->visibility != 'visible') {\n            $this->PDFAXwarnings[] = \"Cannot set visibility to anything other than full when using PDFA or PDFX\";\n            return '';\n        } elseif (!$this->PDFA && !$this->PDFX) {\n            $this->pdf_version = '1.5';\n        }\n        if ($this->visibility != 'visible') {\n            $this->writer->write('EMC');\n            $this->hasOC = intval($this->hasOC);\n        }\n        if ($v == 'printonly') {\n            $this->writer->write('/OC /OC1 BDC');\n            $this->hasOC = ($this->hasOC | 1);\n        } elseif ($v == 'screenonly') {\n            $this->writer->write('/OC /OC2 BDC');\n            $this->hasOC = ($this->hasOC | 2);\n        } elseif ($v == 'hidden') {\n            $this->writer->write('/OC /OC3 BDC');\n            $this->hasOC = ($this->hasOC | 4);\n        } elseif ($v != 'visible') {\n            throw new \\Mpdf\\MpdfException('Incorrect visibility: ' . $v);\n        }\n        $this->visibility = $v;\n    }\n\n    function Open()\n    {\n        // Begin document\n        if ($this->state == 0) {\n            // Was is function _begindoc()\n            // Start document\n            $this->state = 1;\n            $this->writer->write('%PDF-' . $this->pdf_version);\n            $this->writer->write('%' . chr(226) . chr(227) . chr(207) . chr(211)); // 4 chars > 128 to show binary file\n        }\n    }\n\n    function Close()\n    {\n        // @log Closing last page\n\n        // Terminate document\n        if ($this->state == 3) {\n            return;\n        }\n        if ($this->page == 0) {\n            $this->AddPage($this->CurOrientation);\n        }\n        if (count($this->cellBorderBuffer)) {\n            $this->printcellbuffer();\n        } // *TABLES*\n        if ($this->tablebuffer) {\n            $this->printtablebuffer();\n        } // *TABLES*\n        /* -- COLUMNS -- */\n\n        if ($this->ColActive) {\n            $this->SetColumns(0);\n            $this->ColActive = 0;\n            if (count($this->columnbuffer)) {\n                $this->printcolumnbuffer();\n            }\n        }\n        /* -- END COLUMNS -- */\n\n        // BODY Backgrounds\n        $s = '';\n\n        $s .= $this->PrintBodyBackgrounds();\n        $s .= $this->PrintPageBackgrounds();\n\n        $this->pages[$this->page] = preg_replace(\n            '/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',\n            \"\\n\" . $s . \"\\n\" . '\\\\1',\n            $this->pages[$this->page]\n        );\n\n        $this->pageBackgrounds = [];\n\n        if ($this->visibility != 'visible') {\n            $this->SetVisibility('visible');\n        }\n\n        $this->EndLayer();\n\n        if (!$this->tableOfContents->TOCmark) { // Page footer\n            $this->InFooter = true;\n            $this->Footer();\n            $this->InFooter = false;\n        }\n\n        if ($this->tableOfContents->TOCmark || count($this->tableOfContents->m_TOC)) {\n            $this->tableOfContents->insertTOC();\n        }\n\n        // *TOC*\n        // Close page\n        $this->_endpage();\n\n        // Close document\n        $this->_enddoc();\n    }\n\n    /* -- BACKGROUNDS -- */\n\n    function _resizeBackgroundImage($imw, $imh, $cw, $ch, $resize, $repx, $repy, $pba = [], $size = [])\n    {\n        // pba is background positioning area (from CSS background-origin) may not always be set [x,y,w,h]\n        // size is from CSS3 background-size - takes precendence over old resize\n        // $w - absolute length or % or auto or cover | contain\n        // $h - absolute length or % or auto or cover | contain\n        if (isset($pba['w'])) {\n            $cw = $pba['w'];\n        }\n        if (isset($pba['h'])) {\n            $ch = $pba['h'];\n        }\n\n        $cw = $cw * Mpdf::SCALE;\n        $ch = $ch * Mpdf::SCALE;\n        if (empty($size) && !$resize) {\n            return [$imw, $imh, $repx, $repy];\n        }\n\n        if (isset($size['w']) && $size['w']) {\n            if ($size['w'] == 'contain') {\n                // Scale the image, while preserving its intrinsic aspect ratio (if any),\n                // to the largest size such that both its width and its height can fit inside the background positioning area.\n                // Same as resize==3\n                $h = $imh * $cw / $imw;\n                $w = $cw;\n                if ($h > $ch) {\n                    $w = $w * $ch / $h;\n                    $h = $ch;\n                }\n            } elseif ($size['w'] == 'cover') {\n                // Scale the image, while preserving its intrinsic aspect ratio (if any),\n                // to the smallest size such that both its width and its height can completely cover the background positioning area.\n                $h = $imh * $cw / $imw;\n                $w = $cw;\n                if ($h < $ch) {\n                    $w = $w * $h / $ch;\n                    $h = $ch;\n                }\n            } else {\n                if (stristr($size['w'], '%')) {\n                    $size['w'] = (float) $size['w'];\n                    $size['w'] /= 100;\n                    $size['w'] = ($cw * $size['w']);\n                }\n                if (stristr($size['h'], '%')) {\n                    $size['h'] = (float) $size['h'];\n                    $size['h'] /= 100;\n                    $size['h'] = ($ch * $size['h']);\n                }\n                if ($size['w'] == 'auto' && $size['h'] == 'auto') {\n                    $w = $imw;\n                    $h = $imh;\n                } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {\n                    $w = $imw * $size['h'] / $imh;\n                    $h = $size['h'];\n                } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {\n                    $h = $imh * $size['w'] / $imw;\n                    $w = $size['w'];\n                } else {\n                    $w = $size['w'];\n                    $h = $size['h'];\n                }\n            }\n            return [$w, $h, $repx, $repy];\n        } elseif ($resize == 1 && $imw > $cw) {\n            $h = $imh * $cw / $imw;\n            return [$cw, $h, $repx, $repy];\n        } elseif ($resize == 2 && $imh > $ch) {\n            $w = $imw * $ch / $imh;\n            return [$w, $ch, $repx, $repy];\n        } elseif ($resize == 3) {\n            $w = $imw;\n            $h = $imh;\n            if ($w > $cw) {\n                $h = $h * $cw / $w;\n                $w = $cw;\n            }\n            if ($h > $ch) {\n                $w = $w * $ch / $h;\n                $h = $ch;\n            }\n            return [$w, $h, $repx, $repy];\n        } elseif ($resize == 4) {\n            $h = $imh * $cw / $imw;\n            return [$cw, $h, $repx, $repy];\n        } elseif ($resize == 5) {\n            $w = $imw * $ch / $imh;\n            return [$w, $ch, $repx, $repy];\n        } elseif ($resize == 6) {\n            return [$cw, $ch, $repx, $repy];\n        }\n        return [$imw, $imh, $repx, $repy];\n    }\n\n    function SetBackground(&$properties, &$maxwidth)\n    {\n        if (isset($properties['BACKGROUND-ORIGIN']) && ($properties['BACKGROUND-ORIGIN'] == 'border-box' || $properties['BACKGROUND-ORIGIN'] == 'content-box')) {\n            $origin = $properties['BACKGROUND-ORIGIN'];\n        } else {\n            $origin = 'padding-box';\n        }\n\n        if (isset($properties['BACKGROUND-SIZE'])) {\n            if (stristr($properties['BACKGROUND-SIZE'], 'contain')) {\n                $bsw = $bsh = 'contain';\n            } elseif (stristr($properties['BACKGROUND-SIZE'], 'cover')) {\n                $bsw = $bsh = 'cover';\n            } else {\n                $bsw = $bsh = 'auto';\n                $sz = preg_split('/\\s+/', trim($properties['BACKGROUND-SIZE']));\n                if (count($sz) == 2) {\n                    $bsw = $sz[0];\n                    $bsh = $sz[1];\n                } else {\n                    $bsw = $sz[0];\n                }\n                if (!stristr($bsw, '%') && !stristr($bsw, 'auto')) {\n                    $bsw = $this->sizeConverter->convert($bsw, $maxwidth, $this->FontSize);\n                }\n                if (!stristr($bsh, '%') && !stristr($bsh, 'auto')) {\n                    $bsh = $this->sizeConverter->convert($bsh, $maxwidth, $this->FontSize);\n                }\n            }\n            $size = ['w' => $bsw, 'h' => $bsh];\n        } else {\n            $size = false;\n        } // mPDF 6\n        if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $properties['BACKGROUND-IMAGE'])) {\n            return ['gradient' => $properties['BACKGROUND-IMAGE'], 'origin' => $origin, 'size' => $size];\n        } else {\n            $file = $properties['BACKGROUND-IMAGE'];\n            $sizesarray = $this->Image($file, 0, 0, 0, 0, '', '', false, false, false, false, true);\n            if (isset($sizesarray['IMAGE_ID'])) {\n                $image_id = $sizesarray['IMAGE_ID'];\n                $orig_w = $sizesarray['WIDTH'] * Mpdf::SCALE;  // in user units i.e. mm\n                $orig_h = $sizesarray['HEIGHT'] * Mpdf::SCALE;  // (using $this->img_dpi)\n                if (isset($properties['BACKGROUND-IMAGE-RESOLUTION'])) {\n                    if (preg_match('/from-image/i', $properties['BACKGROUND-IMAGE-RESOLUTION']) && isset($sizesarray['set-dpi']) && $sizesarray['set-dpi'] > 0) {\n                        $orig_w *= $this->img_dpi / $sizesarray['set-dpi'];\n                        $orig_h *= $this->img_dpi / $sizesarray['set-dpi'];\n                    } elseif (preg_match('/(\\d+)dpi/i', $properties['BACKGROUND-IMAGE-RESOLUTION'], $m)) {\n                        $dpi = $m[1];\n                        if ($dpi > 0) {\n                            $orig_w *= $this->img_dpi / $dpi;\n                            $orig_h *= $this->img_dpi / $dpi;\n                        }\n                    }\n                }\n                $x_repeat = true;\n                $y_repeat = true;\n                if (isset($properties['BACKGROUND-REPEAT'])) {\n                    if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-x') {\n                        $y_repeat = false;\n                    }\n                    if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-y') {\n                        $x_repeat = false;\n                    }\n                }\n                $x_pos = 0;\n                $y_pos = 0;\n                if (isset($properties['BACKGROUND-POSITION'])) {\n                    $ppos = preg_split('/\\s+/', $properties['BACKGROUND-POSITION']);\n                    $x_pos = $ppos[0];\n                    $y_pos = $ppos[1];\n                    if (!stristr($x_pos, '%')) {\n                        $x_pos = $this->sizeConverter->convert($x_pos, $maxwidth, $this->FontSize);\n                    }\n                    if (!stristr($y_pos, '%')) {\n                        $y_pos = $this->sizeConverter->convert($y_pos, $maxwidth, $this->FontSize);\n                    }\n                }\n                if (isset($properties['BACKGROUND-IMAGE-RESIZE'])) {\n                    $resize = $properties['BACKGROUND-IMAGE-RESIZE'];\n                } else {\n                    $resize = 0;\n                }\n                if (isset($properties['BACKGROUND-IMAGE-OPACITY'])) {\n                    $opacity = $properties['BACKGROUND-IMAGE-OPACITY'];\n                } else {\n                    $opacity = 1;\n                }\n                return ['image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $sizesarray['itype'], 'origin' => $origin, 'size' => $size];\n            }\n        }\n        return false;\n    }\n\n    /* -- END BACKGROUNDS -- */\n\n    function PrintBodyBackgrounds()\n    {\n        $s = '';\n        $clx = 0;\n        $cly = 0;\n        $clw = $this->w;\n        $clh = $this->h;\n        // If using bleed and trim margins in paged media\n        if ($this->pageDim[$this->page]['outer_width_LR'] || $this->pageDim[$this->page]['outer_width_TB']) {\n            $clx = $this->pageDim[$this->page]['outer_width_LR'] - $this->pageDim[$this->page]['bleedMargin'];\n            $cly = $this->pageDim[$this->page]['outer_width_TB'] - $this->pageDim[$this->page]['bleedMargin'];\n            $clw = $this->w - 2 * $clx;\n            $clh = $this->h - 2 * $cly;\n        }\n\n        if ($this->bodyBackgroundColor) {\n            $s .= 'q ' . $this->SetFColor($this->bodyBackgroundColor, true) . \"\\n\";\n            if ($this->bodyBackgroundColor[0] == 5) { // RGBa\n                $s .= $this->SetAlpha(ord($this->bodyBackgroundColor[4]) / 100, 'Normal', true, 'F') . \"\\n\";\n            } elseif ($this->bodyBackgroundColor[0] == 6) { // CMYKa\n                $s .= $this->SetAlpha(ord($this->bodyBackgroundColor[5]) / 100, 'Normal', true, 'F') . \"\\n\";\n            }\n            $s .= sprintf('%.3F %.3F %.3F %.3F re f Q', ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . \"\\n\";\n        }\n\n        /* -- BACKGROUNDS -- */\n        if ($this->bodyBackgroundGradient) {\n            $g = $this->gradient->parseBackgroundGradient($this->bodyBackgroundGradient);\n            if ($g) {\n                $s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, (isset($g['gradtype']) ? $g['gradtype'] : null), $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);\n            }\n        }\n        if ($this->bodyBackgroundImage) {\n            if (isset($this->bodyBackgroundImage['gradient']) && $this->bodyBackgroundImage['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->bodyBackgroundImage['gradient'])) {\n                $g = $this->gradient->parseMozGradient($this->bodyBackgroundImage['gradient']);\n                if ($g) {\n                    $s .= $this->gradient->Gradient($clx, $cly, $clw, $clh, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);\n                }\n            } elseif ($this->bodyBackgroundImage['image_id']) { // Background pattern\n                $n = count($this->patterns) + 1;\n                // If using resize, uses TrimBox (not including the bleed)\n                [$orig_w, $orig_h, $x_repeat, $y_repeat] = $this->_resizeBackgroundImage($this->bodyBackgroundImage['orig_w'], $this->bodyBackgroundImage['orig_h'], $clw, $clh, $this->bodyBackgroundImage['resize'], $this->bodyBackgroundImage['x_repeat'], $this->bodyBackgroundImage['y_repeat']);\n\n                $this->patterns[$n] = ['x' => $clx, 'y' => $cly, 'w' => $clw, 'h' => $clh, 'pgh' => $this->h, 'image_id' => $this->bodyBackgroundImage['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $this->bodyBackgroundImage['x_pos'], 'y_pos' => $this->bodyBackgroundImage['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $this->bodyBackgroundImage['itype']];\n                if (($this->bodyBackgroundImage['opacity'] > 0 || $this->bodyBackgroundImage['opacity'] === '0') && $this->bodyBackgroundImage['opacity'] < 1) {\n                    $opac = $this->SetAlpha($this->bodyBackgroundImage['opacity'], 'Normal', true);\n                } else {\n                    $opac = '';\n                }\n                $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, ($clx * Mpdf::SCALE), ($cly * Mpdf::SCALE), $clw * Mpdf::SCALE, $clh * Mpdf::SCALE) . \"\\n\";\n            }\n        }\n        /* -- END BACKGROUNDS -- */\n        return $s;\n    }\n\n    function _setClippingPath($clx, $cly, $clw, $clh)\n    {\n        $s = ' q 0 w '; // Line width=0\n        $s .= sprintf('%.3F %.3F m ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // start point TL before the arc\n        $s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BL\n        $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly + $clh)) * Mpdf::SCALE); // line to BR\n        $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TR\n        $s .= sprintf('%.3F %.3F l ', ($clx) * Mpdf::SCALE, ($this->h - ($cly)) * Mpdf::SCALE); // line to TL\n        $s .= ' W n '; // Ends path no-op & Sets the clipping path\n        return $s;\n    }\n\n    function PrintPageBackgrounds($adjustmenty = 0)\n    {\n        $s = '';\n\n        ksort($this->pageBackgrounds);\n\n        foreach ($this->pageBackgrounds as $bl => $pbs) {\n\n            foreach ($pbs as $pb) {\n\n                if ((!isset($pb['image_id']) && !isset($pb['gradient'])) || isset($pb['shadowonly'])) { // Background colour or boxshadow\n\n                    if ($pb['z-index'] > 0) {\n                        $this->current_layer = $pb['z-index'];\n                        $s .= \"\\n\" . '/OCBZ-index /ZI' . $pb['z-index'] . ' BDC' . \"\\n\";\n                    }\n\n                    if ($pb['visibility'] != 'visible') {\n                        if ($pb['visibility'] == 'printonly') {\n                            $s .= '/OC /OC1 BDC' . \"\\n\";\n                        } elseif ($pb['visibility'] == 'screenonly') {\n                            $s .= '/OC /OC2 BDC' . \"\\n\";\n                        } elseif ($pb['visibility'] == 'hidden') {\n                            $s .= '/OC /OC3 BDC' . \"\\n\";\n                        }\n                    }\n\n                    // Box shadow\n                    if (isset($pb['shadow']) && $pb['shadow']) {\n                        $s .= $pb['shadow'] . \"\\n\";\n                    }\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= $pb['clippath'] . \"\\n\";\n                    }\n\n                    $s .= 'q ' . $this->SetFColor($pb['col'], true) . \"\\n\";\n\n                    if ($pb['col'] && $pb['col'][0] === '5') { // RGBa\n                        $s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . \"\\n\";\n                    } elseif ($pb['col'] && $pb['col'][0] === '6') { // CMYKa\n                        $s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . \"\\n\";\n                    }\n\n                    $s .= sprintf('%.3F %.3F %.3F %.3F re f Q', $pb['x'] * Mpdf::SCALE, ($this->h - $pb['y']) * Mpdf::SCALE, $pb['w'] * Mpdf::SCALE, -$pb['h'] * Mpdf::SCALE) . \"\\n\";\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= 'Q' . \"\\n\";\n                    }\n\n                    if ($pb['visibility'] != 'visible') {\n                        $s .= 'EMC' . \"\\n\";\n                    }\n\n                    if ($pb['z-index'] > 0) {\n                        $s .= \"\\n\" . 'EMCBZ-index' . \"\\n\";\n                        $this->current_layer = 0;\n                    }\n                }\n            }\n\n            /* -- BACKGROUNDS -- */\n            foreach ($pbs as $pb) {\n\n                if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {\n\n                    if ($pb['z-index'] > 0) {\n                        $this->current_layer = $pb['z-index'];\n                        $s .= \"\\n\" . '/OCGZ-index /ZI' . $pb['z-index'] . ' BDC' . \"\\n\";\n                    }\n\n                    if ($pb['visibility'] != 'visible') {\n                        if ($pb['visibility'] == 'printonly') {\n                            $s .= '/OC /OC1 BDC' . \"\\n\";\n                        } elseif ($pb['visibility'] == 'screenonly') {\n                            $s .= '/OC /OC2 BDC' . \"\\n\";\n                        } elseif ($pb['visibility'] == 'hidden') {\n                            $s .= '/OC /OC3 BDC' . \"\\n\";\n                        }\n                    }\n\n                }\n\n                if (isset($pb['gradient']) && $pb['gradient']) {\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= $pb['clippath'] . \"\\n\";\n                    }\n\n                    $s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= 'Q' . \"\\n\";\n                    }\n\n                } elseif (isset($pb['image_id']) && $pb['image_id']) { // Background Image\n\n                    $pb['y'] -= $adjustmenty;\n                    $pb['h'] += $adjustmenty;\n                    $n = count($this->patterns) + 1;\n\n                    [$orig_w, $orig_h, $x_repeat, $y_repeat] = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat'], $pb['bpa'], $pb['size']);\n\n                    $this->patterns[$n] = ['x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype'], 'bpa' => $pb['bpa']];\n\n                    $x = $pb['x'] * Mpdf::SCALE;\n                    $y = ($this->h - $pb['y']) * Mpdf::SCALE;\n                    $w = $pb['w'] * Mpdf::SCALE;\n                    $h = -$pb['h'] * Mpdf::SCALE;\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= $pb['clippath'] . \"\\n\";\n                    }\n\n                    if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern\n\n                        $iw = $pb['orig_w'] / Mpdf::SCALE;\n                        $ih = $pb['orig_h'] / Mpdf::SCALE;\n\n                        $w = $pb['w'];\n                        $h = $pb['h'];\n                        $x0 = $pb['x'];\n                        $y0 = $pb['y'];\n\n                        if (isset($pb['bpa']) && $pb['bpa']) {\n                            $w = $pb['bpa']['w'];\n                            $h = $pb['bpa']['h'];\n                            $x0 = $pb['bpa']['x'];\n                            $y0 = $pb['bpa']['y'];\n                        }\n\n                        if (isset($pb['size']['w']) && $pb['size']['w']) {\n                            $size = $pb['size'];\n\n                            if ($size['w'] == 'contain') {\n                                // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest\n                                // size such that both its width and its height can fit inside the background positioning area.\n                                // Same as resize==3\n                                $ih = $ih * $pb['bpa']['w'] / $iw;\n                                $iw = $pb['bpa']['w'];\n                                if ($ih > $pb['bpa']['h']) {\n                                    $iw = $iw * $pb['bpa']['h'] / $ih;\n                                    $ih = $pb['bpa']['h'];\n                                }\n                            } elseif ($size['w'] == 'cover') {\n                                // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest\n                                // size such that both its width and its height can completely cover the background positioning area.\n                                $ih = $ih * $pb['bpa']['w'] / $iw;\n                                $iw = $pb['bpa']['w'];\n                                if ($ih < $pb['bpa']['h']) {\n                                    $iw = $iw * $ih / $pb['bpa']['h'];\n                                    $ih = $pb['bpa']['h'];\n                                }\n                            } else {\n\n                                if (NumericString::containsPercentChar($size['w'])) {\n                                    $size['w'] = NumericString::removePercentChar($size['w']);\n                                    $size['w'] /= 100;\n                                    $size['w'] = ($pb['bpa']['w'] * $size['w']);\n                                }\n\n                                if (NumericString::containsPercentChar($size['h'])) {\n                                    $size['h'] = NumericString::removePercentChar($size['h']);\n                                    $size['h'] /= 100;\n                                    $size['h'] = ($pb['bpa']['h'] * $size['h']);\n                                }\n\n                                if ($size['w'] == 'auto' && $size['h'] == 'auto') {\n                                    $iw = $iw;\n                                    $ih = $ih;\n                                } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {\n                                    $iw = $iw * $size['h'] / $ih;\n                                    $ih = $size['h'];\n                                } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {\n                                    $ih = $ih * $size['w'] / $iw;\n                                    $iw = $size['w'];\n                                } else {\n                                    $iw = $size['w'];\n                                    $ih = $size['h'];\n                                }\n                            }\n                        }\n\n                        // Number to repeat\n                        if ($pb['x_repeat']) {\n                            $nx = ceil($pb['w'] / $iw) + 1;\n                        } else {\n                            $nx = 1;\n                        }\n\n                        if ($pb['y_repeat']) {\n                            $ny = ceil($pb['h'] / $ih) + 1;\n                        } else {\n                            $ny = 1;\n                        }\n\n                        $x_pos = $pb['x_pos'];\n                        if (stristr($x_pos, '%')) {\n                            $x_pos = (float) $x_pos;\n                            $x_pos /= 100;\n                            $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);\n                        }\n\n                        $y_pos = $pb['y_pos'];\n\n                        if (stristr($y_pos, '%')) {\n                            $y_pos = (float) $y_pos;\n                            $y_pos /= 100;\n                            $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);\n                        }\n\n                        if ($nx > 1) {\n                            while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {\n                                $x_pos -= $iw;\n                            }\n                        }\n\n                        if ($ny > 1) {\n                            while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {\n                                $y_pos -= $ih;\n                            }\n                        }\n\n                        for ($xi = 0; $xi < $nx; $xi++) {\n                            for ($yi = 0; $yi < $ny; $yi++) {\n                                $x = $x0 + $x_pos + ($iw * $xi);\n                                $y = $y0 + $y_pos + ($ih * $yi);\n                                if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {\n                                    $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);\n                                } else {\n                                    $opac = '';\n                                }\n                                $s .= sprintf(\"q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q\", $opac, $iw * Mpdf::SCALE, $ih * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $ih)) * Mpdf::SCALE, $pb['image_id']) . \"\\n\";\n                            }\n                        }\n\n                    } else {\n                        if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {\n                            $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);\n                        } else {\n                            $opac = '';\n                        }\n                        $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . \"\\n\";\n                    }\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= 'Q' . \"\\n\";\n                    }\n                }\n\n                if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {\n                    if ($pb['visibility'] != 'visible') {\n                        $s .= 'EMC' . \"\\n\";\n                    }\n\n                    if ($pb['z-index'] > 0) {\n                        $s .= \"\\n\" . 'EMCGZ-index' . \"\\n\";\n                        $this->current_layer = 0;\n                    }\n                }\n            }\n            /* -- END BACKGROUNDS -- */\n        }\n\n        return $s;\n    }\n\n    function PrintTableBackgrounds($adjustmenty = 0)\n    {\n        $s = '';\n        /* -- BACKGROUNDS -- */\n        ksort($this->tableBackgrounds);\n        foreach ($this->tableBackgrounds as $bl => $pbs) {\n            foreach ($pbs as $pb) {\n                if ((!isset($pb['gradient']) || !$pb['gradient']) && (!isset($pb['image_id']) || !$pb['image_id'])) {\n                    $s .= 'q ' . $this->SetFColor($pb['col'], true) . \"\\n\";\n                    if ($pb['col'][0] == 5) { // RGBa\n                        $s .= $this->SetAlpha(ord($pb['col'][4]) / 100, 'Normal', true, 'F') . \"\\n\";\n                    } elseif ($pb['col'][0] == 6) { // CMYKa\n                        $s .= $this->SetAlpha(ord($pb['col'][5]) / 100, 'Normal', true, 'F') . \"\\n\";\n                    }\n                    $s .= sprintf('%.3F %.3F %.3F %.3F re %s Q', $pb['x'] * Mpdf::SCALE, ($this->h - $pb['y']) * Mpdf::SCALE, $pb['w'] * Mpdf::SCALE, -$pb['h'] * Mpdf::SCALE, 'f') . \"\\n\";\n                }\n                if (isset($pb['gradient']) && $pb['gradient']) {\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= $pb['clippath'] . \"\\n\";\n                    }\n                    $s .= $this->gradient->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= 'Q' . \"\\n\";\n                    }\n                }\n                if (isset($pb['image_id']) && $pb['image_id']) { // Background pattern\n                    $pb['y'] -= $adjustmenty;\n                    $pb['h'] += $adjustmenty;\n                    $n = count($this->patterns) + 1;\n                    [$orig_w, $orig_h, $x_repeat, $y_repeat] = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat']);\n                    $this->patterns[$n] = ['x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype']];\n                    $x = $pb['x'] * Mpdf::SCALE;\n                    $y = ($this->h - $pb['y']) * Mpdf::SCALE;\n                    $w = $pb['w'] * Mpdf::SCALE;\n                    $h = -$pb['h'] * Mpdf::SCALE;\n\n                    // mPDF 5.7.3\n                    if (($this->writingHTMLfooter || $this->writingHTMLheader) && (!isset($pb['clippath']) || $pb['clippath'] == '')) {\n                        // Set clipping path\n                        $pb['clippath'] = sprintf(' q 0 w %.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l W n ', $x, $y, $x, $y + $h, $x + $w, $y + $h, $x + $w, $y, $x, $y);\n                    }\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= $pb['clippath'] . \"\\n\";\n                    }\n\n                    // mPDF 5.7.3\n                    if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern\n                        $iw = $pb['orig_w'] / Mpdf::SCALE;\n                        $ih = $pb['orig_h'] / Mpdf::SCALE;\n\n                        $w = $pb['w'];\n                        $h = $pb['h'];\n                        $x0 = $pb['x'];\n                        $y0 = $pb['y'];\n\n                        if (isset($pb['bpa']) && $pb['bpa']) {\n                            $w = $pb['bpa']['w'];\n                            $h = $pb['bpa']['h'];\n                            $x0 = $pb['bpa']['x'];\n                            $y0 = $pb['bpa']['y'];\n                        } // At present 'bpa' (background page area) is not set for tablebackgrounds - only pagebackgrounds\n                        // For now, just set it as:\n                        else {\n                            $pb['bpa'] = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];\n                        }\n\n                        if (isset($pb['size']['w']) && $pb['size']['w']) {\n                            $size = $pb['size'];\n\n                            if ($size['w'] == 'contain') {\n                                // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.\n                                // Same as resize==3\n                                $ih = $ih * $pb['bpa']['w'] / $iw;\n                                $iw = $pb['bpa']['w'];\n                                if ($ih > $pb['bpa']['h']) {\n                                    $iw = $iw * $pb['bpa']['h'] / $ih;\n                                    $ih = $pb['bpa']['h'];\n                                }\n                            } elseif ($size['w'] == 'cover') {\n                                // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.\n                                $ih = $ih * $pb['bpa']['w'] / $iw;\n                                $iw = $pb['bpa']['w'];\n                                if ($ih < $pb['bpa']['h']) {\n                                    $iw = $iw * $ih / $pb['bpa']['h'];\n                                    $ih = $pb['bpa']['h'];\n                                }\n                            } else {\n                                if (NumericString::containsPercentChar($size['w'])) {\n                                    $size['w'] = NumericString::removePercentChar($size['w']);\n                                    $size['w'] /= 100;\n                                    $size['w'] = ($pb['bpa']['w'] * $size['w']);\n                                }\n                                if (NumericString::containsPercentChar($size['h'])) {\n                                    $size['h'] = NumericString::removePercentChar($size['h']);\n                                    $size['h'] /= 100;\n                                    $size['h'] = ($pb['bpa']['h'] * $size['h']);\n                                }\n                                if ($size['w'] == 'auto' && $size['h'] == 'auto') {\n                                    $iw = $iw;\n                                    $ih = $ih;\n                                } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {\n                                    $iw = $iw * $size['h'] / $ih;\n                                    $ih = $size['h'];\n                                } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {\n                                    $ih = $ih * $size['w'] / $iw;\n                                    $iw = $size['w'];\n                                } else {\n                                    $iw = $size['w'];\n                                    $ih = $size['h'];\n                                }\n                            }\n                        }\n\n                        // Number to repeat\n                        if (isset($pb['x_repeat']) && $pb['x_repeat']) {\n                            $nx = ceil($pb['w'] / $iw) + 1;\n                        } else {\n                            $nx = 1;\n                        }\n                        if (isset($pb['y_repeat']) && $pb['y_repeat']) {\n                            $ny = ceil($pb['h'] / $ih) + 1;\n                        } else {\n                            $ny = 1;\n                        }\n\n                        $x_pos = $pb['x_pos'];\n                        if (NumericString::containsPercentChar($x_pos)) {\n                            $x_pos = NumericString::removePercentChar($x_pos);\n                            $x_pos /= 100;\n                            $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);\n                        }\n                        $y_pos = $pb['y_pos'];\n                        if (NumericString::containsPercentChar($y_pos)) {\n                            $y_pos = NumericString::removePercentChar($y_pos);\n                            $y_pos /= 100;\n                            $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);\n                        }\n                        if ($nx > 1) {\n                            while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {\n                                $x_pos -= $iw;\n                            }\n                        }\n                        if ($ny > 1) {\n                            while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {\n                                $y_pos -= $ih;\n                            }\n                        }\n                        for ($xi = 0; $xi < $nx; $xi++) {\n                            for ($yi = 0; $yi < $ny; $yi++) {\n                                $x = $x0 + $x_pos + ($iw * $xi);\n                                $y = $y0 + $y_pos + ($ih * $yi);\n                                if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {\n                                    $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);\n                                } else {\n                                    $opac = '';\n                                }\n                                $s .= sprintf(\"q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q\", $opac, $iw * Mpdf::SCALE, $ih * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $ih)) * Mpdf::SCALE, $pb['image_id']) . \"\\n\";\n                            }\n                        }\n                    } else {\n                        if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {\n                            $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);\n                        } else {\n                            $opac = '';\n                        }\n                        $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . \"\\n\";\n                    }\n\n                    if (isset($pb['clippath']) && $pb['clippath']) {\n                        $s .= 'Q' . \"\\n\";\n                    }\n                }\n            }\n        }\n        /* -- END BACKGROUNDS -- */\n        return $s;\n    }\n\n    function BeginLayer($id)\n    {\n        if ($this->current_layer > 0) {\n            $this->EndLayer();\n        }\n        if ($id < 1) {\n            return false;\n        }\n        if (!isset($this->layers[$id])) {\n            $this->layers[$id] = ['name' => 'Layer ' . ($id)];\n            if (($this->PDFA || $this->PDFX)) {\n                $this->PDFAXwarnings[] = \"Cannot use layers when using PDFA or PDFX\";\n                return '';\n            } elseif (!$this->PDFA && !$this->PDFX) {\n                $this->pdf_version = '1.5';\n            }\n        }\n        $this->current_layer = $id;\n        $this->writer->write('/OCZ-index /ZI' . $id . ' BDC');\n\n        $this->pageoutput[$this->page] = [];\n    }\n\n    function EndLayer()\n    {\n        if ($this->current_layer > 0) {\n            $this->writer->write('EMCZ-index');\n            $this->current_layer = 0;\n        }\n    }\n\n    function AddPageByArray($a)\n    {\n        if (!is_array($a)) {\n            $a = [];\n        }\n\n        $orientation = (isset($a['orientation']) ? $a['orientation'] : '');\n        $condition = (isset($a['condition']) ? $a['condition'] : (isset($a['type']) ? $a['type'] : ''));\n        $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');\n        $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');\n        $suppress = (isset($a['suppress']) ? $a['suppress'] : '');\n        $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));\n        $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));\n        $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));\n        $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));\n        $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));\n        $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));\n        $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));\n        $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));\n        $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));\n        $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));\n        $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));\n        $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));\n        $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));\n        $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));\n        $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));\n        $newformat = (isset($a['newformat']) ? $a['newformat'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));\n\n        $this->AddPage($orientation, $condition, $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);\n    }\n\n    // mPDF 6 pagebreaktype\n    function _preForcedPagebreak($pagebreaktype)\n    {\n        if ($pagebreaktype == 'cloneall') {\n            // Close any open block tags\n            $arr = [];\n            $ai = 0;\n            for ($b = $this->blklvl; $b > 0; $b--) {\n                $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);\n            }\n            if ($this->blklvl == 0 && !empty($this->textbuffer)) { // Output previously buffered content\n                $this->printbuffer($this->textbuffer, 1);\n                $this->textbuffer = [];\n            }\n        } elseif ($pagebreaktype == 'clonebycss') {\n            // Close open block tags whilst box-decoration-break==clone\n            $arr = [];\n            $ai = 0;\n            for ($b = $this->blklvl; $b > 0; $b--) {\n                if (isset($this->blk[$b]['box_decoration_break']) && $this->blk[$b]['box_decoration_break'] == 'clone') {\n                    $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);\n                } else {\n                    if ($b == $this->blklvl && !empty($this->textbuffer)) { // Output previously buffered content\n                        $this->printbuffer($this->textbuffer, 1);\n                        $this->textbuffer = [];\n                    }\n                    break;\n                }\n            }\n        } elseif (!empty($this->textbuffer)) { // Output previously buffered content\n            $this->printbuffer($this->textbuffer, 1);\n            $this->textbuffer = [];\n        }\n    }\n\n    // mPDF 6 pagebreaktype\n    function _postForcedPagebreak($pagebreaktype, $startpage, $save_blk, $save_blklvl)\n    {\n        if ($pagebreaktype == 'cloneall') {\n            $this->blk = [];\n            $this->blk[0] = $save_blk[0];\n            // Re-open block tags\n            $this->blklvl = 0;\n            $arr = [];\n            $i = 0;\n            for ($b = 1; $b <= $save_blklvl; $b++) {\n                $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);\n            }\n        } elseif ($pagebreaktype == 'clonebycss') {\n            $this->blk = [];\n            $this->blk[0] = $save_blk[0];\n            // Don't re-open tags for lowest level elements - so need to do some adjustments\n            for ($b = 1; $b <= $this->blklvl; $b++) {\n                $this->blk[$b] = $save_blk[$b];\n                $this->blk[$b]['startpage'] = 0;\n                $this->blk[$b]['y0'] = $this->y; // ?? $this->tMargin\n                if (($this->page - $startpage) % 2) {\n                    if (isset($this->blk[$b]['x0'])) {\n                        $this->blk[$b]['x0'] += $this->MarginCorrection;\n                    } else {\n                        $this->blk[$b]['x0'] = $this->MarginCorrection;\n                    }\n                }\n                // for Float DIV\n                $this->blk[$b]['marginCorrected'][$this->page] = true;\n            }\n\n            // Re-open block tags for any that have box_decoration_break==clone\n            $arr = [];\n            $i = 0;\n            for ($b = $this->blklvl + 1; $b <= $save_blklvl; $b++) {\n                if ($b < $this->blklvl) {\n                    $this->lastblocklevelchange = -1;\n                }\n                $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);\n            }\n            if ($this->blk[$this->blklvl]['box_decoration_break'] != 'clone') {\n                $this->lastblocklevelchange = -1;\n            }\n        } else {\n            $this->lastblocklevelchange = -1;\n        }\n    }\n\n    function AddPage(\n        $orientation = '',\n        $condition = '',\n        $resetpagenum = '',\n        $pagenumstyle = '',\n        $suppress = '',\n        $mgl = '',\n        $mgr = '',\n        $mgt = '',\n        $mgb = '',\n        $mgh = '',\n        $mgf = '',\n        $ohname = '',\n        $ehname = '',\n        $ofname = '',\n        $efname = '',\n        $ohvalue = 0,\n        $ehvalue = 0,\n        $ofvalue = 0,\n        $efvalue = 0,\n        $pagesel = '',\n        $newformat = ''\n    ) {\n        /* -- CSS-FLOAT -- */\n        // Float DIV\n        // Cannot do with columns on, or if any change in page orientation/margins etc.\n        // If next page already exists - i.e background /headers and footers already written\n        if ($this->state > 0 && $this->page < count($this->pages)) {\n            $bak_cml = $this->cMarginL;\n            $bak_cmr = $this->cMarginR;\n            $bak_dw = $this->divwidth;\n            // Paint Div Border if necessary\n            if ($this->blklvl > 0) {\n                $save_tr = $this->table_rotate; // *TABLES*\n                $this->table_rotate = 0; // *TABLES*\n                if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0']) {\n                    $this->blk[$this->blklvl]['startpage'] ++;\n                }\n                if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table']) {\n                    $toplvl = $this->blklvl;\n                } else {\n                    $toplvl = $this->blklvl - 1;\n                }\n                $sy = $this->y;\n                for ($bl = 1; $bl <= $toplvl; $bl++) {\n                    $this->PaintDivBB('pagebottom', 0, $bl);\n                }\n                $this->y = $sy;\n                $this->table_rotate = $save_tr; // *TABLES*\n            }\n            $s = $this->PrintPageBackgrounds();\n\n            // Writes after the marker so not overwritten later by page background etc.\n            $this->pages[$this->page] = preg_replace(\n                '/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/',\n                '\\\\1' . \"\\n\" . $s . \"\\n\",\n                $this->pages[$this->page]\n            );\n\n            $this->pageBackgrounds = [];\n            $family = $this->FontFamily;\n            $style = $this->FontStyle;\n            $size = $this->FontSizePt;\n            $lw = $this->LineWidth;\n            $dc = $this->DrawColor;\n            $fc = $this->FillColor;\n            $tc = $this->TextColor;\n            $cf = $this->ColorFlag;\n\n            $this->printfloatbuffer();\n\n            // Move to next page\n            $this->page++;\n\n            $this->ResetMargins();\n            $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);\n            $this->x = $this->lMargin;\n            $this->y = $this->tMargin;\n            $this->FontFamily = '';\n            $this->writer->write('2 J');\n            $this->LineWidth = $lw;\n            $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));\n\n            if ($family) {\n                $this->SetFont($family, $style, $size, true, true);\n            }\n\n            $this->DrawColor = $dc;\n\n            if ($dc != $this->defDrawColor) {\n                $this->writer->write($dc);\n            }\n\n            $this->FillColor = $fc;\n\n            if ($fc != $this->defFillColor) {\n                $this->writer->write($fc);\n            }\n\n            $this->TextColor = $tc;\n            $this->ColorFlag = $cf;\n\n            for ($bl = 1; $bl <= $this->blklvl; $bl++) {\n                $this->blk[$bl]['y0'] = $this->y;\n                // Don't correct more than once for background DIV containing a Float\n                if (!isset($this->blk[$bl]['marginCorrected'][$this->page])) {\n                    if (isset($this->blk[$bl]['x0'])) {\n                        $this->blk[$bl]['x0'] += $this->MarginCorrection;\n                    } else {\n                        $this->blk[$bl]['x0'] = $this->MarginCorrection;\n                    }\n                }\n                $this->blk[$bl]['marginCorrected'][$this->page] = true;\n            }\n\n            $this->cMarginL = $bak_cml;\n            $this->cMarginR = $bak_cmr;\n            $this->divwidth = $bak_dw;\n\n            return '';\n        }\n        /* -- END CSS-FLOAT -- */\n\n        // Start a new page\n        if ($this->state == 0) {\n            $this->Open();\n        }\n\n        $bak_cml = $this->cMarginL;\n        $bak_cmr = $this->cMarginR;\n        $bak_dw = $this->divwidth;\n\n        $bak_lh = $this->lineheight;\n\n        $orientation = substr(strtoupper($orientation), 0, 1);\n        $condition = strtoupper($condition);\n\n\n        if ($condition == 'E') { // only adds new page if needed to create an Even page\n            if (!$this->mirrorMargins || ($this->page) % 2 == 0) {\n                return false;\n            }\n        } elseif ($condition == 'O') { // only adds new page if needed to create an Odd page\n            if (!$this->mirrorMargins || ($this->page) % 2 == 1) {\n                return false;\n            }\n        } elseif ($condition == 'NEXT-EVEN') { // always adds at least one new page to create an Even page\n            if (!$this->mirrorMargins) {\n                $condition = '';\n            } else {\n                if ($pagesel) {\n                    $pbch = $pagesel;\n                    $pagesel = '';\n                } // *CSS-PAGE*\n                else {\n                    $pbch = false;\n                } // *CSS-PAGE*\n                $this->AddPage($this->CurOrientation, 'O');\n                $this->extrapagebreak = true; // mPDF 6 pagebreaktype\n                if ($pbch) {\n                    $pagesel = $pbch;\n                } // *CSS-PAGE*\n                $condition = '';\n            }\n        } elseif ($condition == 'NEXT-ODD') { // always adds at least one new page to create an Odd page\n            if (!$this->mirrorMargins) {\n                $condition = '';\n            } else {\n                if ($pagesel) {\n                    $pbch = $pagesel;\n                    $pagesel = '';\n                } // *CSS-PAGE*\n                else {\n                    $pbch = false;\n                } // *CSS-PAGE*\n                $this->AddPage($this->CurOrientation, 'E');\n                $this->extrapagebreak = true; // mPDF 6 pagebreaktype\n                if ($pbch) {\n                    $pagesel = $pbch;\n                } // *CSS-PAGE*\n                $condition = '';\n            }\n        }\n\n        if ($resetpagenum || $pagenumstyle || $suppress) {\n            $this->PageNumSubstitutions[] = ['from' => ($this->page + 1), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];\n        }\n\n        $save_tr = $this->table_rotate; // *TABLES*\n        $this->table_rotate = 0; // *TABLES*\n        $save_kwt = $this->kwt;\n        $this->kwt = 0;\n        $save_layer = $this->current_layer;\n        $save_vis = $this->visibility;\n\n        if ($this->visibility != 'visible') {\n            $this->SetVisibility('visible');\n        }\n\n        $this->EndLayer();\n\n        // Paint Div Border if necessary\n        // PAINTS BACKGROUND COLOUR OR BORDERS for DIV - DISABLED FOR COLUMNS (cf. AcceptPageBreak) AT PRESENT in ->PaintDivBB\n        if (!$this->ColActive && $this->blklvl > 0) {\n            if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0'] && !$this->extrapagebreak) { // mPDF 6 pagebreaktype\n                if (isset($this->blk[$this->blklvl]['startpage'])) {\n                    $this->blk[$this->blklvl]['startpage'] ++;\n                } else {\n                    $this->blk[$this->blklvl]['startpage'] = 1;\n                }\n            }\n            if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table'] || $this->extrapagebreak) {\n                $toplvl = $this->blklvl;\n            } // mPDF 6 pagebreaktype\n            else {\n                $toplvl = $this->blklvl - 1;\n            }\n            $sy = $this->y;\n            for ($bl = 1; $bl <= $toplvl; $bl++) {\n                if (isset($this->blk[$bl]['z-index']) && $this->blk[$bl]['z-index'] > 0) {\n                    $this->BeginLayer($this->blk[$bl]['z-index']);\n                }\n                if (isset($this->blk[$bl]['visibility']) && $this->blk[$bl]['visibility'] && $this->blk[$bl]['visibility'] != 'visible') {\n                    $this->SetVisibility($this->blk[$bl]['visibility']);\n                }\n                $this->PaintDivBB('pagebottom', 0, $bl);\n            }\n            $this->y = $sy;\n            // RESET block y0 and x0 - see below\n        }\n        $this->extrapagebreak = false; // mPDF 6 pagebreaktype\n\n        if ($this->visibility != 'visible') {\n            $this->SetVisibility('visible');\n        }\n\n        $this->EndLayer();\n\n        // BODY Backgrounds\n        if ($this->page > 0) {\n            $s = '';\n            $s .= $this->PrintBodyBackgrounds();\n\n            $s .= $this->PrintPageBackgrounds();\n            $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', \"\\n\" . $s . \"\\n\" . '\\\\1', $this->pages[$this->page]);\n            $this->pageBackgrounds = [];\n        }\n\n        $save_kt = $this->keep_block_together;\n        $this->keep_block_together = 0;\n\n        $save_cols = false;\n\n        /* -- COLUMNS -- */\n        if ($this->ColActive) {\n            $save_cols = true;\n            $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off\n            $this->SetColumns(0);\n        }\n        /* -- END COLUMNS -- */\n\n        $family = $this->FontFamily;\n        $style = $this->FontStyle;\n        $size = $this->FontSizePt;\n        $this->ColumnAdjust = true; // enables column height adjustment for the page\n        $lw = $this->LineWidth;\n        $dc = $this->DrawColor;\n        $fc = $this->FillColor;\n        $tc = $this->TextColor;\n        $cf = $this->ColorFlag;\n        if ($this->page > 0) {\n            // Page footer\n            $this->InFooter = true;\n\n            $this->Reset();\n            $this->pageoutput[$this->page] = [];\n\n            $this->Footer();\n            // Close page\n            $this->_endpage();\n        }\n\n        // Start new page\n        $this->_beginpage($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);\n\n        if ($this->docTemplate) {\n            $currentReaderId = $this->currentReaderId;\n\n            $pagecount = $this->setSourceFile($this->docTemplate);\n            if (($this->page - $this->docTemplateStart) > $pagecount) {\n                if ($this->docTemplateContinue) {\n                    $tplIdx = $this->importPage($pagecount);\n                    $this->useTemplate($tplIdx);\n                }\n            } else {\n                $tplIdx = $this->importPage(($this->page - $this->docTemplateStart));\n                $this->useTemplate($tplIdx);\n            }\n\n            $this->currentReaderId = $currentReaderId;\n        }\n\n        if ($this->pageTemplate) {\n            $this->useTemplate($this->pageTemplate);\n        }\n\n        // Tiling Patterns\n        $this->writer->write('___PAGE___START' . $this->uniqstr);\n        $this->writer->write('___BACKGROUND___PATTERNS' . $this->uniqstr);\n        $this->writer->write('___HEADER___MARKER' . $this->uniqstr);\n        $this->pageBackgrounds = [];\n\n        // Set line cap style to square\n        $this->SetLineCap(2);\n        // Set line width\n        $this->LineWidth = $lw;\n        $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));\n        // Set font\n        if ($family) {\n            $this->SetFont($family, $style, $size, true, true); // forces write\n        }\n\n        // Set colors\n        $this->DrawColor = $dc;\n        if ($dc != $this->defDrawColor) {\n            $this->writer->write($dc);\n        }\n        $this->FillColor = $fc;\n        if ($fc != $this->defFillColor) {\n            $this->writer->write($fc);\n        }\n        $this->TextColor = $tc;\n        $this->ColorFlag = $cf;\n\n        // Page header\n        $this->Header();\n\n        // Restore line width\n        if ($this->LineWidth != $lw) {\n            $this->LineWidth = $lw;\n            $this->writer->write(sprintf('%.3F w', $lw * Mpdf::SCALE));\n        }\n        // Restore font\n        if ($family) {\n            $this->SetFont($family, $style, $size, true, true); // forces write\n        }\n\n        // Restore colors\n        if ($this->DrawColor != $dc) {\n            $this->DrawColor = $dc;\n            $this->writer->write($dc);\n        }\n        if ($this->FillColor != $fc) {\n            $this->FillColor = $fc;\n            $this->writer->write($fc);\n        }\n        $this->TextColor = $tc;\n        $this->ColorFlag = $cf;\n        $this->InFooter = false;\n\n        if ($save_layer > 0) {\n            $this->BeginLayer($save_layer);\n        }\n\n        if ($save_vis != 'visible') {\n            $this->SetVisibility($save_vis);\n        }\n\n        /* -- COLUMNS -- */\n        if ($save_cols) {\n            // Restore columns\n            $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);\n        }\n        if ($this->ColActive) {\n            $this->SetCol(0);\n        }\n        /* -- END COLUMNS -- */\n\n\n        // RESET BLOCK BORDER TOP\n        if (!$this->ColActive) {\n            for ($bl = 1; $bl <= $this->blklvl; $bl++) {\n                $this->blk[$bl]['y0'] = $this->y;\n                if (isset($this->blk[$bl]['x0'])) {\n                    $this->blk[$bl]['x0'] += $this->MarginCorrection;\n                } else {\n                    $this->blk[$bl]['x0'] = $this->MarginCorrection;\n                }\n                // Added mPDF 3.0 Float DIV\n                $this->blk[$bl]['marginCorrected'][$this->page] = true;\n            }\n        }\n\n\n        $this->table_rotate = $save_tr; // *TABLES*\n        $this->kwt = $save_kwt;\n\n        $this->keep_block_together = $save_kt;\n\n        $this->cMarginL = $bak_cml;\n        $this->cMarginR = $bak_cmr;\n        $this->divwidth = $bak_dw;\n\n        $this->lineheight = $bak_lh;\n    }\n\n    /**\n     * Get current page number\n     *\n     * @return int\n     */\n    function PageNo()\n    {\n        return $this->page;\n    }\n\n    function AddSpotColorsFromFile($file)\n    {\n        $colors = @file($file);\n        if (!$colors) {\n            throw new \\Mpdf\\MpdfException(\"Cannot load spot colors file - \" . $file);\n        }\n        foreach ($colors as $sc) {\n            [$name, $c, $m, $y, $k] = preg_split(\"/\\t/\", $sc);\n            $c = intval($c);\n            $m = intval($m);\n            $y = intval($y);\n            $k = intval($k);\n            $this->AddSpotColor($name, $c, $m, $y, $k);\n        }\n    }\n\n    function AddSpotColor($name, $c, $m, $y, $k)\n    {\n        $name = strtoupper(trim($name));\n        if (!isset($this->spotColors[$name])) {\n            $i = count($this->spotColors) + 1;\n            $this->spotColors[$name] = ['i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k];\n            $this->spotColorIDs[$i] = $name;\n        }\n    }\n\n    function SetColor($col, $type = '')\n    {\n        $out = '';\n        if (!$col) {\n            return '';\n        } // mPDF 6\n        if ($col[0] == 3 || $col[0] == 5) { // RGB / RGBa\n            $out = sprintf('%.3F %.3F %.3F rg', ord($col[1]) / 255, ord($col[2]) / 255, ord($col[3]) / 255);\n        } elseif ($col[0] == 1) { // GRAYSCALE\n            $out = sprintf('%.3F g', ord($col[1]) / 255);\n        } elseif ($col[0] == 2) { // SPOT COLOR\n            $out = sprintf('/CS%d cs %.3F scn', ord($col[1]), ord($col[2]) / 100);\n        } elseif ($col[0] == 4 || $col[0] == 6) { // CMYK / CMYKa\n            $out = sprintf('%.3F %.3F %.3F %.3F k', ord($col[1]) / 100, ord($col[2]) / 100, ord($col[3]) / 100, ord($col[4]) / 100);\n        }\n        if ($type == 'Draw') {\n            $out = strtoupper($out);\n        } // e.g. rg => RG\n        elseif ($type == 'CodeOnly') {\n            $out = preg_replace('/\\s(rg|g|k)/', '', $out);\n        }\n        return $out;\n    }\n\n    function SetDColor($col, $return = false)\n    {\n        $out = $this->SetColor($col, 'Draw');\n        if ($return) {\n            return $out;\n        }\n        if ($out == '') {\n            return '';\n        }\n        $this->DrawColor = $out;\n        if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['DrawColor']) && $this->pageoutput[$this->page]['DrawColor'] != $this->DrawColor) || !isset($this->pageoutput[$this->page]['DrawColor']))) {\n            $this->writer->write($this->DrawColor);\n        }\n        $this->pageoutput[$this->page]['DrawColor'] = $this->DrawColor;\n    }\n\n    function SetFColor($col, $return = false)\n    {\n        $out = $this->SetColor($col, 'Fill');\n        if ($return) {\n            return $out;\n        }\n        if ($out == '') {\n            return '';\n        }\n        $this->FillColor = $out;\n        $this->ColorFlag = ($out != $this->TextColor);\n        if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor']))) {\n            $this->writer->write($this->FillColor);\n        }\n        $this->pageoutput[$this->page]['FillColor'] = $this->FillColor;\n    }\n\n    function SetTColor($col, $return = false)\n    {\n        $out = $this->SetColor($col, 'Text');\n        if ($return) {\n            return $out;\n        }\n        if ($out == '') {\n            return '';\n        }\n        $this->TextColor = $out;\n        $this->ColorFlag = ($this->FillColor != $out);\n    }\n\n    function SetDrawColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)\n    {\n        // Set color for all stroking operations\n        $col = [];\n        if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {\n            $col = $this->colorConverter->convert($r, $this->PDFAXwarnings);\n        } elseif ($col4 == -1) {\n            $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);\n        } else {\n            $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);\n        }\n        $out = $this->SetDColor($col, $return);\n        return $out;\n    }\n\n    function SetFillColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)\n    {\n        // Set color for all filling operations\n        $col = [];\n        if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {\n            $col = $this->colorConverter->convert($r, $this->PDFAXwarnings);\n        } elseif ($col4 == -1) {\n            $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);\n        } else {\n            $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);\n        }\n        $out = $this->SetFColor($col, $return);\n        return $out;\n    }\n\n    function SetTextColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)\n    {\n        // Set color for text\n        $col = [];\n        if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {\n            $col = $this->colorConverter->convert($r, $this->PDFAXwarnings);\n        } elseif ($col4 == -1) {\n            $col = $this->colorConverter->convert('rgb(' . $r . ',' . $g . ',' . $b . ')', $this->PDFAXwarnings);\n        } else {\n            $col = $this->colorConverter->convert('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')', $this->PDFAXwarnings);\n        }\n        $out = $this->SetTColor($col, $return);\n        return $out;\n    }\n\n    function _getCharWidth(&$cw, $u, $isdef = true)\n    {\n        $w = 0;\n\n        if ($u == 0) {\n            $w = false;\n        } elseif (isset($cw[$u * 2 + 1])) {\n            $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);\n        }\n\n        if ($w == 65535) {\n            return 0;\n        } elseif ($w) {\n            return $w;\n        } elseif ($isdef) {\n            return false;\n        } else {\n            return 0;\n        }\n    }\n\n    function _charDefined(&$cw, $u)\n    {\n        $w = 0;\n        if ($u == 0) {\n            return false;\n        }\n        if (isset($cw[$u * 2 + 1])) {\n            $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);\n        }\n\n        return (bool) $w;\n    }\n\n    function GetCharWidthCore($c)\n    {\n        // Get width of a single character in the current Core font\n        $c = (string) $c;\n        $w = 0;\n        // Soft Hyphens chr(173)\n        if ($c == chr(173) && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {\n            return 0;\n        } elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($c)])) {  // mPDF 5.7.1\n            $charw = $this->CurrentFont['cw'][chr($this->upperCase[ord($c)])];\n            if ($charw !== false) {\n                $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;\n                $w+=$charw;\n            }\n        } elseif (isset($this->CurrentFont['cw'][$c])) {\n            $w += $this->CurrentFont['cw'][$c];\n        } elseif (isset($this->CurrentFont['cw'][ord($c)])) {\n            $w += $this->CurrentFont['cw'][ord($c)];\n        }\n        $w *= ($this->FontSize / 1000);\n        if ($this->minwSpacing || $this->fixedlSpacing) {\n            if ($c == ' ') {\n                $nb_spaces = 1;\n            } else {\n                $nb_spaces = 0;\n            }\n            $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);\n        }\n        return ($w);\n    }\n\n    function GetCharWidthNonCore($c, $addSubset = true)\n    {\n        // Get width of a single character in the current Non-Core font\n        $c = (string) $c;\n        $w = 0;\n        $unicode = $this->UTF8StringToArray($c, $addSubset);\n        $char = $unicode[0];\n        /* -- CJK-FONTS -- */\n        if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts\n            if ($char == 173) {\n                return 0;\n            } // Soft Hyphens\n            elseif (isset($this->CurrentFont['cw'][$char])) {\n                $w+=$this->CurrentFont['cw'][$char];\n            } elseif (isset($this->CurrentFont['MissingWidth'])) {\n                $w += $this->CurrentFont['MissingWidth'];\n            } else {\n                $w += 500;\n            }\n        } else {\n            /* -- END CJK-FONTS -- */\n            if ($char == 173) {\n                return 0;\n            } // Soft Hyphens\n            elseif (($this->textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) { // mPDF 5.7.1\n                $charw = $this->_getCharWidth($this->CurrentFont['cw'], $this->upperCase[$char]);\n                if ($charw !== false) {\n                    $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;\n                    $w+=$charw;\n                } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {\n                    $w += $this->CurrentFont['desc']['MissingWidth'];\n                } elseif (isset($this->CurrentFont['MissingWidth'])) {\n                    $w += $this->CurrentFont['MissingWidth'];\n                } else {\n                    $w += 500;\n                }\n            } else {\n                $charw = $this->_getCharWidth($this->CurrentFont['cw'], $char);\n                if ($charw !== false) {\n                    $w+=$charw;\n                } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {\n                    $w += $this->CurrentFont['desc']['MissingWidth'];\n                } elseif (isset($this->CurrentFont['MissingWidth'])) {\n                    $w += $this->CurrentFont['MissingWidth'];\n                } else {\n                    $w += 500;\n                }\n            }\n        } // *CJK-FONTS*\n        $w *= ($this->FontSize / 1000);\n        if ($this->minwSpacing || $this->fixedlSpacing) {\n            if ($c == ' ') {\n                $nb_spaces = 1;\n            } else {\n                $nb_spaces = 0;\n            }\n            $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);\n        }\n        return ($w);\n    }\n\n    function GetCharWidth($c, $addSubset = true)\n    {\n        if (!$this->usingCoreFont) {\n            return $this->GetCharWidthNonCore($c, $addSubset);\n        } else {\n            return $this->GetCharWidthCore($c);\n        }\n    }\n\n    function GetStringWidth($s, $addSubset = true, $OTLdata = false, $textvar = 0, $includeKashida = false)\n    {\n        // mPDF 5.7.1\n        // Get width of a string in the current font\n        $s = (string) $s;\n        $cw = &$this->CurrentFont['cw'];\n        $w = 0;\n        $kerning = 0;\n        $lastchar = 0;\n        $nb_carac = 0;\n        $nb_spaces = 0;\n        $kashida = 0;\n        // mPDF ITERATION\n        if ($this->iterationCounter) {\n            $s = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\\\1', $s);\n        }\n        if (!$this->usingCoreFont) {\n            $discards = substr_count($s, \"\\xc2\\xad\"); // mPDF 6 soft hyphens [U+00AD]\n            $unicode = $this->UTF8StringToArray($s, $addSubset);\n            if ($this->minwSpacing || $this->fixedlSpacing) {\n                $nb_spaces = mb_substr_count($s, ' ', $this->mb_enc);\n                $nb_carac = count($unicode) - $discards; // mPDF 6\n                // mPDF 5.7.1\n                // Use GPOS OTL\n                if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                    if (isset($OTLdata['group']) && $OTLdata['group']) {\n                        $nb_carac -= substr_count($OTLdata['group'], 'M');\n                    }\n                }\n            }\n            /* -- CJK-FONTS -- */\n            if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts\n                foreach ($unicode as $char) {\n                    if ($char == 0x00AD) {\n                        continue;\n                    } // mPDF 6 soft hyphens [U+00AD]\n                    if (isset($cw[$char])) {\n                        $w+=$cw[$char];\n                    } elseif (isset($this->CurrentFont['MissingWidth'])) {\n                        $w += $this->CurrentFont['MissingWidth'];\n                    } else {\n                        $w += 500;\n                    }\n                }\n            } else {\n                /* -- END CJK-FONTS -- */\n                foreach ($unicode as $i => $char) {\n                    if ($char == 0x00AD) {\n                        continue;\n                    } // mPDF 6 soft hyphens [U+00AD]\n                    if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[$char])) {\n                        $charw = $this->_getCharWidth($cw, $this->upperCase[$char]);\n                        if ($charw !== false) {\n                            $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;\n                            $w+=$charw;\n                        } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {\n                            $w += $this->CurrentFont['desc']['MissingWidth'];\n                        } elseif (isset($this->CurrentFont['MissingWidth'])) {\n                            $w += $this->CurrentFont['MissingWidth'];\n                        } else {\n                            $w += 500;\n                        }\n                    } else {\n                        $charw = $this->_getCharWidth($cw, $char);\n                        if ($charw !== false) {\n                            $w+=$charw;\n                        } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {\n                            $w += $this->CurrentFont['desc']['MissingWidth'];\n                        } elseif (isset($this->CurrentFont['MissingWidth'])) {\n                            $w += $this->CurrentFont['MissingWidth'];\n                        } else {\n                            $w += 500;\n                        }\n                        // mPDF 5.7.1\n                        // Use GPOS OTL\n                        // ...GetStringWidth...\n                        if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata)) {\n                            if (isset($OTLdata['GPOSinfo'][$i]['wDir']) && $OTLdata['GPOSinfo'][$i]['wDir'] == 'RTL') {\n                                if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceR']) && $OTLdata['GPOSinfo'][$i]['XAdvanceR']) {\n                                    $w += $OTLdata['GPOSinfo'][$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                                }\n                            } else {\n                                if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceL']) && $OTLdata['GPOSinfo'][$i]['XAdvanceL']) {\n                                    $w += $OTLdata['GPOSinfo'][$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                                }\n                            }\n                            // Kashida from GPOS\n                            // Kashida is set as an absolute length value (already set as a proportion based on useKashida %)\n                            if ($includeKashida && isset($OTLdata['GPOSinfo'][$i]['kashida_space']) && $OTLdata['GPOSinfo'][$i]['kashida_space']) {\n                                $kashida += $OTLdata['GPOSinfo'][$i]['kashida_space'];\n                            }\n                        }\n                        if (($textvar & TextVars::FC_KERNING) && $lastchar) {\n                            if (isset($this->CurrentFont['kerninfo'][$lastchar][$char])) {\n                                $kerning += $this->CurrentFont['kerninfo'][$lastchar][$char];\n                            }\n                        }\n                        $lastchar = $char;\n                    }\n                }\n            } // *CJK-FONTS*\n        } else {\n            if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {\n                $s = str_replace(chr(173), '', $s);\n            }\n            $nb_carac = $l = strlen($s);\n            if ($this->minwSpacing || $this->fixedlSpacing) {\n                $nb_spaces = substr_count($s, ' ');\n            }\n            for ($i = 0; $i < $l; $i++) {\n                if (($textvar & TextVars::FC_SMALLCAPS) && isset($this->upperCase[ord($s[$i])])) {  // mPDF 5.7.1\n                    $charw = $cw[chr($this->upperCase[ord($s[$i])])];\n                    if ($charw !== false) {\n                        $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;\n                        $w+=$charw;\n                    }\n                } elseif (isset($cw[$s[$i]])) {\n                    $w += $cw[$s[$i]];\n                } elseif (isset($cw[ord($s[$i])])) {\n                    $w += $cw[ord($s[$i])];\n                }\n                if (($textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1\n                    if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]])) {\n                        $kerning += $this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]];\n                    }\n                }\n            }\n        }\n        unset($cw);\n        if ($textvar & TextVars::FC_KERNING) {\n            $w += $kerning;\n        } // mPDF 5.7.1\n        $w *= ($this->FontSize / 1000);\n        $w += (($nb_carac + $nb_spaces) * $this->fixedlSpacing) + ($nb_spaces * $this->minwSpacing);\n        $w += $kashida / Mpdf::SCALE;\n\n        return ($w);\n    }\n\n    function SetLineWidth($width)\n    {\n        // Set line width\n        $this->LineWidth = $width;\n        $lwout = (sprintf('%.3F w', $width * Mpdf::SCALE));\n        if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineWidth']) && $this->pageoutput[$this->page]['LineWidth'] != $lwout) || !isset($this->pageoutput[$this->page]['LineWidth']))) {\n            $this->writer->write($lwout);\n        }\n        $this->pageoutput[$this->page]['LineWidth'] = $lwout;\n    }\n\n    function Line($x1, $y1, $x2, $y2)\n    {\n        // Draw a line\n        $this->writer->write(sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($this->h - $y2) * Mpdf::SCALE));\n    }\n\n    function Arrow($x1, $y1, $x2, $y2, $headsize = 3, $fill = 'B', $angle = 25)\n    {\n        // F == fill // S == stroke // B == stroke and fill\n        // angle = splay of arrowhead - 1 - 89 degrees\n        if ($fill == 'F') {\n            $fill = 'f';\n        } elseif ($fill == 'FD' or $fill == 'DF' or $fill == 'B') {\n            $fill = 'B';\n        } else {\n            $fill = 'S';\n        }\n        $a = atan2(($y2 - $y1), ($x2 - $x1));\n        $b = $a + deg2rad($angle);\n        $c = $a - deg2rad($angle);\n        $x3 = $x2 - ($headsize * cos($b));\n        $y3 = $this->h - ($y2 - ($headsize * sin($b)));\n        $x4 = $x2 - ($headsize * cos($c));\n        $y4 = $this->h - ($y2 - ($headsize * sin($c)));\n\n        $x5 = $x3 - ($x3 - $x4) / 2; // mid point of base of arrowhead - to join arrow line to\n        $y5 = $y3 - ($y3 - $y4) / 2;\n\n        $s = '';\n        $s .= sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * Mpdf::SCALE, ($this->h - $y1) * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE);\n        $this->writer->write($s);\n\n        $s = '';\n        $s .= sprintf('%.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l ', $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE, $x3 * Mpdf::SCALE, $y3 * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($this->h - $y2) * Mpdf::SCALE, $x4 * Mpdf::SCALE, $y4 * Mpdf::SCALE, $x5 * Mpdf::SCALE, $y5 * Mpdf::SCALE);\n        $s .= $fill;\n        $this->writer->write($s);\n    }\n\n    function Rect($x, $y, $w, $h, $style = '')\n    {\n        // Draw a rectangle\n        if ($style == 'F') {\n            $op = 'f';\n        } elseif ($style == 'FD' or $style == 'DF') {\n            $op = 'B';\n        } else {\n            $op = 'S';\n        }\n        $this->writer->write(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$h * Mpdf::SCALE, $op));\n    }\n\n    function AddFontDirectory($directory)\n    {\n        $this->fontDir[] = $directory;\n        $this->fontFileFinder->setDirectories($this->fontDir);\n    }\n\n    function AddFont($family, $style = '')\n    {\n        if (empty($family)) {\n            return;\n        }\n\n        $family = strtolower($family);\n        $style = strtoupper($style);\n        $style = str_replace('U', '', $style);\n\n        if ($style == 'IB') {\n            $style = 'BI';\n        }\n\n        $fontkey = $family . $style;\n\n        // check if the font has been already added\n        if (isset($this->fonts[$fontkey])) {\n            return;\n        }\n\n        /* -- CJK-FONTS -- */\n        if (in_array($family, $this->available_CJK_fonts)) {\n            if (empty($this->Big5_widths)) {\n                require __DIR__ . '/../data/CJKdata.php';\n            }\n            $this->AddCJKFont($family); // don't need to add style\n            return;\n        }\n        /* -- END CJK-FONTS -- */\n\n        if ($this->usingCoreFont) {\n            throw new \\Mpdf\\MpdfException(\"mPDF Error - problem with Font management\");\n        }\n\n        $stylekey = $style;\n        if (!$style) {\n            $stylekey = 'R';\n        }\n\n        if (!isset($this->fontdata[$family][$stylekey]) || !$this->fontdata[$family][$stylekey]) {\n            throw new \\Mpdf\\MpdfException(sprintf('Font \"%s%s%s\" is not supported', $family, $style ? ' - ' : '', $style));\n        }\n\n        /* Setup defaults */\n        $font = [\n            'name' => '',\n            'type' => '',\n            'desc' => '',\n            'panose' => '',\n            'unitsPerEm' => '',\n            'up' => '',\n            'ut' => '',\n            'strs' => '',\n            'strp' => '',\n            'sip' => false,\n            'smp' => false,\n            'useOTL' => 0,\n            'fontmetrics' => '',\n            'haskerninfo' => false,\n            'haskernGPOS' => false,\n            'hassmallcapsGSUB' => false,\n            'BMPselected' => false,\n            'GSUBScriptLang' => [],\n            'GSUBFeatures' => [],\n            'GSUBLookups' => [],\n            'GPOSScriptLang' => [],\n            'GPOSFeatures' => [],\n            'GPOSLookups' => [],\n            'rtlPUAstr' => '',\n        ];\n\n        $fontCacheFilename = $fontkey . '.mtx.json';\n        if ($this->fontCache->jsonHas($fontCacheFilename)) {\n            $font = $this->fontCache->jsonLoad($fontCacheFilename);\n        }\n\n        $ttffile = $this->fontFileFinder->findFontFile($this->fontdata[$family][$stylekey]);\n        $ttfstat = stat($ttffile);\n\n        $TTCfontID = isset($this->fontdata[$family]['TTCfontID'][$stylekey]) ? isset($this->fontdata[$family]['TTCfontID'][$stylekey]) : 0;\n        $fontUseOTL = isset($this->fontdata[$family]['useOTL']) ? $this->fontdata[$family]['useOTL'] : false;\n        $BMPonly = in_array($family, $this->BMPonly) ? true : false;\n\n        $regenerate = false;\n        if ($BMPonly && !$font['BMPselected']) {\n            $regenerate = true;\n        } elseif (!$BMPonly && $font['BMPselected']) {\n            $regenerate = true;\n        }\n\n        if ($fontUseOTL && $font['useOTL'] != $fontUseOTL) {\n            $regenerate = true;\n            $font['useOTL'] = $fontUseOTL;\n        } elseif (!$fontUseOTL && $font['useOTL']) {\n            $regenerate = true;\n            $font['useOTL'] = 0;\n        }\n\n        if ($this->fontDescriptor != $font['fontmetrics']) {\n            $regenerate = true;\n        } // mPDF 6\n\n        if (empty($font['name']) || $font['originalsize'] != $ttfstat['size'] || $regenerate) {\n            $generator = new MetricsGenerator($this->fontCache, $this->fontDescriptor);\n\n            $generator->generateMetrics(\n                $ttffile,\n                $ttfstat,\n                $fontkey,\n                $TTCfontID,\n                $this->debugfonts,\n                $BMPonly,\n                $font['useOTL'],\n                $fontUseOTL\n            );\n\n            $font = $this->fontCache->jsonLoad($fontCacheFilename);\n            $cw = $this->fontCache->load($fontkey . '.cw.dat');\n            $glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');\n        } else {\n            if ($this->fontCache->has($fontkey . '.cw.dat')) {\n                $cw = $this->fontCache->load($fontkey . '.cw.dat');\n            }\n\n            if ($this->fontCache->has($fontkey . '.gid.dat')) {\n                $glyphIDtoUni = $this->fontCache->load($fontkey . '.gid.dat');\n            }\n        }\n\n        if (isset($this->fontdata[$family]['sip-ext']) && $this->fontdata[$family]['sip-ext']) {\n            $sipext = $this->fontdata[$family]['sip-ext'];\n        } else {\n            $sipext = '';\n        }\n\n        // Override with values from config_font.php\n        if (isset($this->fontdata[$family]['Ascent']) && $this->fontdata[$family]['Ascent']) {\n            $desc['Ascent'] = $this->fontdata[$family]['Ascent'];\n        }\n        if (isset($this->fontdata[$family]['Descent']) && $this->fontdata[$family]['Descent']) {\n            $desc['Descent'] = $this->fontdata[$family]['Descent'];\n        }\n        if (isset($this->fontdata[$family]['Leading']) && $this->fontdata[$family]['Leading']) {\n            $desc['Leading'] = $this->fontdata[$family]['Leading'];\n        }\n\n        $i = count($this->fonts) + $this->extraFontSubsets + 1;\n\n        $this->fonts[$fontkey] = [\n            'i' => $i,\n            'name' => $font['name'],\n            'type' => $font['type'],\n            'desc' => $font['desc'],\n            'panose' => $font['panose'],\n            'unitsPerEm' => $font['unitsPerEm'],\n            'up' => $font['up'],\n            'ut' => $font['ut'],\n            'strs' => $font['strs'],\n            'strp' => $font['strp'],\n            'cw' => $cw,\n            'ttffile' => $ttffile,\n            'fontkey' => $fontkey,\n            'used' => false,\n            'sip' => $font['sip'],\n            'sipext' => $sipext,\n            'smp' => $font['smp'],\n            'TTCfontID' => $TTCfontID,\n            'useOTL' => $fontUseOTL,\n            'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false),\n            'GSUBScriptLang' => $font['GSUBScriptLang'],\n            'GSUBFeatures' => $font['GSUBFeatures'],\n            'GSUBLookups' => $font['GSUBLookups'],\n            'GPOSScriptLang' => $font['GPOSScriptLang'],\n            'GPOSFeatures' => $font['GPOSFeatures'],\n            'GPOSLookups' => $font['GPOSLookups'],\n            'rtlPUAstr' => $font['rtlPUAstr'],\n            'glyphIDtoUni' => $glyphIDtoUni,\n            'haskerninfo' => $font['haskerninfo'],\n            'haskernGPOS' => $font['haskernGPOS'],\n            'hassmallcapsGSUB' => $font['hassmallcapsGSUB'],\n        ];\n\n\n        if (!$font['sip'] && !$font['smp']) {\n            $subsetRange = range(32, 127);\n            $this->fonts[$fontkey]['subset'] = array_combine($subsetRange, $subsetRange);\n        } else {\n            $this->fonts[$fontkey]['subsets'] = [0 => range(0, 127)];\n            $this->fonts[$fontkey]['subsetfontids'] = [$i];\n        }\n\n        if ($font['haskerninfo']) {\n            $this->fonts[$fontkey]['kerninfo'] = $font['kerninfo'];\n        }\n\n        $this->FontFiles[$fontkey] = [\n            'length1' => $font['originalsize'],\n            'type' => 'TTF',\n            'ttffile' => $ttffile,\n            'sip' => $font['sip'],\n            'smp' => $font['smp'],\n        ];\n\n        unset($cw);\n    }\n\n    function SetFont($family, $style = '', $size = 0, $write = true, $forcewrite = false)\n    {\n        $family = strtolower($family);\n\n        if (!$this->onlyCoreFonts) {\n            if ($family == 'sans' || $family == 'sans-serif') {\n                $family = $this->sans_fonts[0];\n            }\n            if ($family == 'serif') {\n                $family = $this->serif_fonts[0];\n            }\n            if ($family == 'mono' || $family == 'monospace') {\n                $family = $this->mono_fonts[0];\n            }\n        }\n\n        if (isset($this->fonttrans[$family]) && $this->fonttrans[$family]) {\n            $family = $this->fonttrans[$family];\n        }\n\n        if ($family == '') {\n            if ($this->FontFamily) {\n                $family = $this->FontFamily;\n            } elseif ($this->default_font) {\n                $family = $this->default_font;\n            } else {\n                throw new \\Mpdf\\MpdfException(\"No font or default font set!\");\n            }\n        }\n\n        $this->ReqFontStyle = $style; // required or requested style - used later for artificial bold/italic\n\n        if (($family == 'csymbol') || ($family == 'czapfdingbats') || ($family == 'ctimes') || ($family == 'ccourier') || ($family == 'chelvetica')) {\n            if ($this->PDFA || $this->PDFX) {\n                if ($family == 'csymbol' || $family == 'czapfdingbats') {\n                    throw new \\Mpdf\\MpdfException(\"Symbol and Zapfdingbats cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a).\");\n                }\n                if ($family == 'ctimes' || $family == 'ccourier' || $family == 'chelvetica') {\n                    if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {\n                        $this->PDFAXwarnings[] = \"Core Adobe font \" . ucfirst($family) . \" cannot be embedded in mPDF, which is required for PDFA1-b or PDFX/1-a. (Embedded font will be substituted.)\";\n                    }\n                    if ($family == 'chelvetica') {\n                        $family = 'sans';\n                    }\n                    if ($family == 'ctimes') {\n                        $family = 'serif';\n                    }\n                    if ($family == 'ccourier') {\n                        $family = 'mono';\n                    }\n                }\n                $this->usingCoreFont = false;\n            } else {\n                $this->usingCoreFont = true;\n            }\n            if ($family == 'csymbol' || $family == 'czapfdingbats') {\n                $style = '';\n            }\n        } else {\n            $this->usingCoreFont = false;\n        }\n\n        // mPDF 5.7.1\n        if ($style) {\n            $style = strtoupper($style);\n            if ($style == 'IB') {\n                $style = 'BI';\n            }\n        }\n        if ($size == 0) {\n            $size = $this->FontSizePt;\n        }\n\n        $fontkey = $family . $style;\n\n        $stylekey = $style;\n        if (!$stylekey) {\n            $stylekey = \"R\";\n        }\n\n        if (!$this->onlyCoreFonts && !$this->usingCoreFont) {\n            if (!isset($this->fonts[$fontkey]) || count($this->default_available_fonts) != count($this->available_unifonts)) { // not already added\n\n                /* -- CJK-FONTS -- */\n                if (in_array($fontkey, $this->available_CJK_fonts)) {\n                    if (!isset($this->fonts[$fontkey])) { // already added\n                        if (empty($this->Big5_widths)) {\n                            require __DIR__ . '/../data/CJKdata.php';\n                        }\n                        $this->AddCJKFont($family); // don't need to add style\n                    }\n                } else { // Test to see if requested font/style is available - or substitute /* -- END CJK-FONTS -- */\n                    if (!in_array($fontkey, $this->available_unifonts)) {\n                        // If font[nostyle] exists - set it\n                        if (in_array($family, $this->available_unifonts)) {\n                            $style = '';\n                        } // elseif only one font available - set it (assumes if only one font available it will not have a style)\n                        elseif (count($this->available_unifonts) == 1) {\n                            $family = $this->available_unifonts[0];\n                            $style = '';\n                        } else {\n                            $found = 0;\n                            // else substitute font of similar type\n                            if (in_array($family, $this->sans_fonts)) {\n                                $i = array_intersect($this->sans_fonts, $this->available_unifonts);\n                                if (count($i)) {\n                                    $i = array_values($i);\n                                    // with requested style if possible\n                                    if (!in_array(($i[0] . $style), $this->available_unifonts)) {\n                                        $style = '';\n                                    }\n                                    $family = $i[0];\n                                    $found = 1;\n                                }\n                            } elseif (in_array($family, $this->serif_fonts)) {\n                                $i = array_intersect($this->serif_fonts, $this->available_unifonts);\n                                if (count($i)) {\n                                    $i = array_values($i);\n                                    // with requested style if possible\n                                    if (!in_array(($i[0] . $style), $this->available_unifonts)) {\n                                        $style = '';\n                                    }\n                                    $family = $i[0];\n                                    $found = 1;\n                                }\n                            } elseif (in_array($family, $this->mono_fonts)) {\n                                $i = array_intersect($this->mono_fonts, $this->available_unifonts);\n                                if (count($i)) {\n                                    $i = array_values($i);\n                                    // with requested style if possible\n                                    if (!in_array(($i[0] . $style), $this->available_unifonts)) {\n                                        $style = '';\n                                    }\n                                    $family = $i[0];\n                                    $found = 1;\n                                }\n                            }\n\n                            if (!$found) {\n                                // set first available font\n                                $fs = $this->available_unifonts[0];\n                                preg_match('/^([a-z_0-9\\-]+)([BI]{0,2})$/', $fs, $fas); // Allow \"-\"\n                                // with requested style if possible\n                                $ws = $fas[1] . $style;\n                                if (in_array($ws, $this->available_unifonts)) {\n                                    $family = $fas[1]; // leave $style as is\n                                } elseif (in_array($fas[1], $this->available_unifonts)) {\n                                    // or without style\n                                    $family = $fas[1];\n                                    $style = '';\n                                } else {\n                                    // or with the style specified\n                                    $family = $fas[1];\n                                    $style = $fas[2];\n                                }\n                            }\n                        }\n                        $fontkey = $family . $style;\n                    }\n                }\n            }\n\n            // try to add font (if not already added)\n            $this->AddFont($family, $style);\n\n            // Test if font is already selected\n            if ($this->FontFamily == $family && $this->FontFamily == $this->currentfontfamily && $this->FontStyle == $style && $this->FontStyle == $this->currentfontstyle && $this->FontSizePt == $size && $this->FontSizePt == $this->currentfontsize && !$forcewrite) {\n                return $family;\n            }\n\n            $fontkey = $family . $style;\n\n            // Select it\n            $this->FontFamily = $family;\n            $this->FontStyle = $style;\n            $this->FontSizePt = $size;\n            $this->FontSize = $size / Mpdf::SCALE;\n            $this->CurrentFont = &$this->fonts[$fontkey];\n            if ($write) {\n                $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));\n                if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {\n                    $this->writer->write($fontout);\n                }\n                $this->pageoutput[$this->page]['Font'] = $fontout;\n            }\n\n            // Added - currentfont (lowercase) used in HTML2PDF\n            $this->currentfontfamily = $family;\n            $this->currentfontsize = $size;\n            $this->currentfontstyle = $style;\n            $this->setMBencoding('UTF-8');\n        } else {  // if using core fonts\n            if ($this->PDFA || $this->PDFX) {\n                throw new \\Mpdf\\MpdfException('Core Adobe fonts cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a) - cannot use option to use core fonts.');\n            }\n            $this->setMBencoding('windows-1252');\n\n            // Test if font is already selected\n            if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {\n                return $family;\n            }\n\n            if (!isset($this->CoreFonts[$fontkey])) {\n                if (in_array($family, $this->serif_fonts)) {\n                    $family = 'ctimes';\n                } elseif (in_array($family, $this->mono_fonts)) {\n                    $family = 'ccourier';\n                } else {\n                    $family = 'chelvetica';\n                }\n                $this->usingCoreFont = true;\n                $fontkey = $family . $style;\n            }\n\n            if (!isset($this->fonts[$fontkey])) {\n                // STANDARD CORE FONTS\n                if (isset($this->CoreFonts[$fontkey])) {\n                    // Load metric file\n                    $file = $family;\n                    if ($family == 'ctimes' || $family == 'chelvetica' || $family == 'ccourier') {\n                        $file .= strtolower($style);\n                    }\n                    require __DIR__ . '/../data/font/' . $file . '.php';\n                    if (!isset($cw)) {\n                        throw new \\Mpdf\\MpdfException(sprintf('Could not include font metric file \"%s\"', $file));\n                    }\n                    $i = count($this->fonts) + $this->extraFontSubsets + 1;\n                    $this->fonts[$fontkey] = ['i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw];\n                    if ($this->useKerning && isset($kerninfo)) {\n                        $this->fonts[$fontkey]['kerninfo'] = $kerninfo;\n                    }\n                } else {\n                    throw new \\Mpdf\\MpdfException(sprintf('Font %s not defined', $fontkey));\n                }\n            }\n\n            // Test if font is already selected\n            if (($this->FontFamily == $family) and ( $this->FontStyle == $style) and ( $this->FontSizePt == $size) && !$forcewrite) {\n                return $family;\n            }\n            // Select it\n            $this->FontFamily = $family;\n            $this->FontStyle = $style;\n            $this->FontSizePt = $size;\n            $this->FontSize = $size / Mpdf::SCALE;\n            $this->CurrentFont = &$this->fonts[$fontkey];\n            if ($write) {\n                $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));\n                if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {\n                    $this->writer->write($fontout);\n                }\n                $this->pageoutput[$this->page]['Font'] = $fontout;\n            }\n            // Added - currentfont (lowercase) used in HTML2PDF\n            $this->currentfontfamily = $family;\n            $this->currentfontsize = $size;\n            $this->currentfontstyle = $style;\n        }\n\n        return $family;\n    }\n\n    function SetFontSize($size, $write = true)\n    {\n        // Set font size in points\n        if ($this->FontSizePt == $size) {\n            return;\n        }\n        $this->FontSizePt = $size;\n        $this->FontSize = $size / Mpdf::SCALE;\n        $this->currentfontsize = $size;\n        if ($write) {\n            $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));\n            // Edited mPDF 3.0\n            if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {\n                $this->writer->write($fontout);\n            }\n            $this->pageoutput[$this->page]['Font'] = $fontout;\n        }\n    }\n\n    function AddLink()\n    {\n        // Create a new internal link\n        $n = count($this->links) + 1;\n        $this->links[$n] = [0, 0];\n        return $n;\n    }\n\n    function SetLink($link, $y = 0, $page = -1)\n    {\n        // Set destination of internal link\n        if ($y == -1) {\n            $y = $this->y;\n        }\n        if ($page == -1) {\n            $page = $this->page;\n        }\n        $this->links[$link] = [$page, $y];\n    }\n\n    function Link($x, $y, $w, $h, $link)\n    {\n        $l = [$x * Mpdf::SCALE, $this->hPt - $y * Mpdf::SCALE, $w * Mpdf::SCALE, $h * Mpdf::SCALE, $link];\n        if ($this->keep_block_together) { // don't write yet\n            return;\n        } elseif ($this->table_rotate) { // *TABLES*\n            $this->tbrot_Links[$this->page][] = $l; // *TABLES*\n            return; // *TABLES*\n        } // *TABLES*\n        elseif ($this->kwt) {\n            $this->kwt_Links[$this->page][] = $l;\n            return;\n        }\n\n        if ($this->writingHTMLheader || $this->writingHTMLfooter) {\n            $this->HTMLheaderPageLinks[] = $l;\n            return;\n        }\n        // Put a link on the page\n        $this->PageLinks[$this->page][] = $l;\n        // Save cross-reference to Column buffer\n        $ref = count($this->PageLinks[$this->page]) - 1; // *COLUMNS*\n        $this->columnLinks[$this->CurrCol][(int) $this->x][(int) $this->y] = $ref; // *COLUMNS*\n    }\n\n    function Text($x, $y, $txt, $OTLdata = [], $textvar = 0, $aixextra = '', $coordsys = '', $return = false)\n    {\n        // Output (or return) a string\n        // Called (internally) by Watermark() & _tableWrite() [rotated cells] & TableHeaderFooter() & WriteText()\n        // Called also from classes/svg.php\n        // Expects Font to be set\n        // Expects input to be mb_encoded if necessary and RTL reversed & OTL processed\n        // ARTIFICIAL BOLD AND ITALIC\n        $s = 'q ';\n        if ($this->falseBoldWeight && strpos($this->ReqFontStyle, \"B\") !== false && strpos($this->FontStyle, \"B\") === false) {\n            $s .= '2 Tr 1 J 1 j ';\n            $s .= sprintf('%.3F w ', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight);\n            $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG\n            if ($this->FillColor != $tc) {\n                $s .= $tc . ' ';\n            }  // stroke (outline) = same colour as text(fill)\n        }\n        if (strpos($this->ReqFontStyle, \"I\") !== false && strpos($this->FontStyle, \"I\") === false) {\n            $aix = '1 0 0.261799 1 %.3F %.3F Tm';\n        } else {\n            $aix = '%.3F %.3F Td';\n        }\n\n        $aix = $aixextra . $aix;\n\n        if ($this->ColorFlag) {\n            $s .= $this->TextColor . ' ';\n        }\n\n        $this->CurrentFont['used'] = true;\n\n        if ($this->usingCoreFont) {\n            $txt2 = str_replace(chr(160), chr(32), $txt);\n        } else {\n            $txt2 = str_replace(chr(194) . chr(160), chr(32), $txt);\n        }\n\n        $px = $x;\n        $py = $y;\n        if ($coordsys != 'SVG') {\n            $px = $x * Mpdf::SCALE;\n            $py = ($this->h - $y) * Mpdf::SCALE;\n        }\n\n\n        /** ************** SIMILAR TO Cell() ************************ */\n\n        // IF corefonts AND NOT SmCaps AND NOT Kerning\n        // Just output text\n        if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {\n            $txt2 = $this->writer->escape($txt2);\n            $s .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);\n        } // IF NOT corefonts [AND NO wordspacing] AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL\n        // Just output text\n        elseif (!$this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {\n            // IF SIP/SMP\n            if ($this->CurrentFont['sip'] || $this->CurrentFont['smp']) {\n                $txt2 = $this->UTF8toSubset($txt2);\n                $s .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);\n            } // NOT SIP/SMP\n            else {\n                $txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false);\n                $txt2 = $this->writer->escape($txt2);\n                $s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);\n            }\n        } // IF NOT corefonts [AND IS wordspacing] AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL\n        // Not required here (cf. Cell() )\n        // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]\n        else {\n            $s .= $this->applyGPOSpdf($txt2, $aix, $px, $py, $OTLdata, $textvar);\n        }\n        /*         * ************** END ************************ */\n\n        $s .= ' ';\n\n        if (($textvar & TextVars::FD_UNDERLINE) && $txt != '') { // mPDF 5.7.1\n            $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG\n            if ($this->FillColor != $c) {\n                $s.= ' ' . $c . ' ';\n            }\n            if (isset($this->CurrentFont['up']) && $this->CurrentFont['up']) {\n                $up = $this->CurrentFont['up'];\n            } else {\n                $up = -100;\n            }\n            $adjusty = (-$up / 1000 * $this->FontSize);\n            if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {\n                $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;\n            } else {\n                $ut = 60 / 1000 * $this->FontSize;\n            }\n            $olw = $this->LineWidth;\n            $s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));\n            $s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);\n            $s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));\n            if ($this->FillColor != $c) {\n                $s.= ' ' . $this->FillColor . ' ';\n            }\n        }\n        // STRIKETHROUGH\n        if (($textvar & TextVars::FD_LINETHROUGH) && $txt != '') { // mPDF 5.7.1\n            $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG\n            if ($this->FillColor != $c) {\n                $s.= ' ' . $c . ' ';\n            }\n            // Superscript and Subscript Y coordinate adjustment (now for striked-through texts)\n            if (isset($this->CurrentFont['desc']['CapHeight']) && $this->CurrentFont['desc']['CapHeight']) {\n                $ch = $this->CurrentFont['desc']['CapHeight'];\n            } else {\n                $ch = 700;\n            }\n            $adjusty = (-$ch / 1000 * $this->FontSize) * 0.35;\n            if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {\n                $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;\n            } else {\n                $ut = 60 / 1000 * $this->FontSize;\n            }\n            $olw = $this->LineWidth;\n            $s .= ' ' . (sprintf(' %.3F w', $ut * Mpdf::SCALE));\n            $s .= ' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);\n            $s .= ' ' . (sprintf(' %.3F w', $olw * Mpdf::SCALE));\n            if ($this->FillColor != $c) {\n                $s.= ' ' . $this->FillColor . ' ';\n            }\n        }\n        $s .= 'Q';\n\n        if ($return) {\n            return $s . \" \\n\";\n        }\n        $this->writer->write($s);\n    }\n\n    /* -- DIRECTW -- */\n\n    function WriteText($x, $y, $txt)\n    {\n        // Output a string using Text() but does encoding and text reversing of RTL\n        $txt = $this->purify_utf8_text($txt);\n        if ($this->text_input_as_HTML) {\n            $txt = $this->all_entities_to_utf8($txt);\n        }\n        if ($this->usingCoreFont) {\n            $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');\n        }\n\n        // DIRECTIONALITY\n        if (preg_match(\"/([\" . $this->pregRTLchars . \"])/u\", $txt)) {\n            $this->biDirectional = true;\n        } // *OTL*\n\n        $textvar = 0;\n        $save_OTLtags = $this->OTLtags;\n        $this->OTLtags = [];\n        if ($this->useKerning) {\n            if ($this->CurrentFont['haskernGPOS']) {\n                $this->OTLtags['Plus'] .= ' kern';\n            } else {\n                $textvar = ($textvar | TextVars::FC_KERNING);\n            }\n        }\n\n        /* -- OTL -- */\n        // Use OTL OpenType Table Layout - GSUB & GPOS\n        if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n            $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);\n            $OTLdata = $this->otl->OTLdata;\n        }\n        /* -- END OTL -- */\n        $this->OTLtags = $save_OTLtags;\n\n        $this->magic_reverse_dir($txt, $this->directionality, $OTLdata);\n\n        $this->Text($x, $y, $txt, $OTLdata, $textvar);\n    }\n\n    function WriteCell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0)\n    {\n        // Output a cell using Cell() but does encoding and text reversing of RTL\n        $txt = $this->purify_utf8_text($txt);\n        if ($this->text_input_as_HTML) {\n            $txt = $this->all_entities_to_utf8($txt);\n        }\n        if ($this->usingCoreFont) {\n            $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');\n        }\n        // DIRECTIONALITY\n        if (preg_match(\"/([\" . $this->pregRTLchars . \"])/u\", $txt)) {\n            $this->biDirectional = true;\n        } // *OTL*\n\n        $textvar = 0;\n        $save_OTLtags = $this->OTLtags;\n        $this->OTLtags = [];\n        if ($this->useKerning) {\n            if ($this->CurrentFont['haskernGPOS']) {\n                $this->OTLtags['Plus'] .= ' kern';\n            } else {\n                $textvar = ($textvar | TextVars::FC_KERNING);\n            }\n        }\n\n        /* -- OTL -- */\n        // Use OTL OpenType Table Layout - GSUB & GPOS\n        if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n            $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);\n            $OTLdata = $this->otl->OTLdata;\n        }\n        /* -- END OTL -- */\n        $this->OTLtags = $save_OTLtags;\n\n        $this->magic_reverse_dir($txt, $this->directionality, $OTLdata);\n\n        $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link, $currentx, 0, 0, 'M', 0, false, $OTLdata, $textvar);\n    }\n\n    /* -- END DIRECTW -- */\n\n    function ResetSpacing()\n    {\n        if ($this->ws != 0) {\n            $this->writer->write('BT 0 Tw ET');\n        }\n        $this->ws = 0;\n        if ($this->charspacing != 0) {\n            $this->writer->write('BT 0 Tc ET');\n        }\n        $this->charspacing = 0;\n    }\n\n    function SetSpacing($cs, $ws)\n    {\n        if (intval($cs * 1000) == 0) {\n            $cs = 0;\n        }\n        if ($cs) {\n            $this->writer->write(sprintf('BT %.3F Tc ET', $cs));\n        } elseif ($this->charspacing != 0) {\n            $this->writer->write('BT 0 Tc ET');\n        }\n        $this->charspacing = $cs;\n        if (intval($ws * 1000) == 0) {\n            $ws = 0;\n        }\n        if ($ws) {\n            $this->writer->write(sprintf('BT %.3F Tw ET', $ws));\n        } elseif ($this->ws != 0) {\n            $this->writer->write('BT 0 Tw ET');\n        }\n        $this->ws = $ws;\n    }\n\n    // WORD SPACING\n    function GetJspacing($nc, $ns, $w, $inclCursive, &$cOTLdata)\n    {\n        $kashida_present = false;\n        $kashida_space = 0;\n        if ($w > 0 && $inclCursive && isset($this->CurrentFont['useKashida']) && $this->CurrentFont['useKashida'] && !empty($cOTLdata)) {\n            for ($c = 0; $c < count($cOTLdata); $c++) {\n                for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {\n                    if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {\n                        $kashida_present = true;\n                        break 2;\n                    }\n                }\n            }\n        }\n\n        if ($kashida_present) {\n            $k_ctr = 0;  // Number of kashida points\n            $k_total = 0;  // Total of kashida values (priority)\n            // Reset word\n            $max_kashida_in_word = 0;\n            $last_kashida_in_word = -1;\n\n            for ($c = 0; $c < count($cOTLdata); $c++) {\n                for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {\n                    if ($cOTLdata[$c]['group'][$i] == 'S') {\n                        // Save from last word\n                        if ($max_kashida_in_word) {\n                            $k_ctr++;\n                            $k_total = $max_kashida_in_word;\n                        }\n                        // Reset word\n                        $max_kashida_in_word = 0;\n                        $last_kashida_in_word = -1;\n                    }\n\n                    if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {\n                        if ($max_kashida_in_word) {\n                            if ($cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > $max_kashida_in_word) {\n                                $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];\n                                $cOTLdata[$c]['GPOSinfo'][$last_kashida_in_word]['kashida'] = 0;\n                                $last_kashida_in_word = $i;\n                            } else {\n                                $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] = 0;\n                            }\n                        } else {\n                            $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];\n                            $last_kashida_in_word = $i;\n                        }\n                    }\n                }\n            }\n            // Save from last word\n            if ($max_kashida_in_word) {\n                $k_ctr++;\n                $k_total = $max_kashida_in_word;\n            }\n\n            // Number of kashida points = $k_ctr\n            // $useKashida is a % value from CurrentFont/config_fonts.php\n            // % ratio divided between word-spacing and kashida-spacing\n            $kashida_space_ratio = intval($this->CurrentFont['useKashida']) / 100;\n\n\n            $kashida_space = $w * $kashida_space_ratio;\n\n            $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);\n            // Only use kashida if each allocated kashida width is > 0.01 x width of a tatweel\n            // Otherwise fontstretch is too small and errors\n            // If not just leave to adjust word-spacing\n            if ($tatw && (($kashida_space / $k_ctr) / $tatw) > 0.01) {\n                for ($c = 0; $c < count($cOTLdata); $c++) {\n                    for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {\n                        if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {\n                            // At this point kashida is a number representing priority (higher number - higher priority)\n                            // We are now going to set it as an actual length\n                            // This shares it equally amongst words:\n                            $cOTLdata[$c]['GPOSinfo'][$i]['kashida_space'] = (1 / $k_ctr) * $kashida_space;\n                        }\n                    }\n                }\n                $w -= $kashida_space;\n            }\n        }\n\n        $ws = 0;\n        $charspacing = 0;\n        $ww = $this->jSWord;\n        $ncx = $nc - 1;\n        if ($nc == 0) {\n            return [0, 0, 0];\n        } // Only word spacing allowed / possible\n        elseif ($this->fixedlSpacing !== false || $inclCursive) {\n            if ($ns) {\n                $ws = $w / $ns;\n            }\n        } elseif ($nc == 1) {\n            $charspacing = $w;\n        } elseif (!$ns) {\n            $charspacing = $w / ($ncx );\n            if (($this->jSmaxChar > 0) && ($charspacing > $this->jSmaxChar)) {\n                $charspacing = $this->jSmaxChar;\n            }\n        } elseif ($ns == ($ncx )) {\n            $charspacing = $w / $ns;\n        } else {\n            if ($this->usingCoreFont) {\n                $cs = ($w * (1 - $this->jSWord)) / ($ncx );\n                if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {\n                    $cs = $this->jSmaxChar;\n                    $ww = 1 - (($cs * ($ncx )) / $w);\n                }\n                $charspacing = $cs;\n                $ws = ($w * ($ww) ) / $ns;\n            } else {\n                $cs = ($w * (1 - $this->jSWord)) / ($ncx - $ns);\n                if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {\n                    $cs = $this->jSmaxChar;\n                    $ww = 1 - (($cs * ($ncx - $ns)) / $w);\n                }\n                $charspacing = $cs;\n                $ws = (($w * ($ww) ) / $ns) - $charspacing;\n            }\n        }\n        return [$charspacing, $ws, $kashida_space];\n    }\n\n    /**\n     * Output a cell\n     *\n     * Expects input to be mb_encoded if necessary and RTL reversed\n     *\n     * @since mPDF 5.7.1\n     */\n    function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0, $lcpaddingL = 0, $lcpaddingR = 0, $valign = 'M', $spanfill = 0, $exactWidth = false, $OTLdata = false, $textvar = 0, $lineBox = false)\n    {\n        // NON_BREAKING SPACE\n        if ($this->usingCoreFont) {\n            $txt = str_replace(chr(160), chr(32), $txt);\n        } else {\n            $txt = str_replace(chr(194) . chr(160), chr(32), $txt);\n        }\n\n        $oldcolumn = $this->CurrCol;\n\n        // Automatic page break\n        // Allows PAGE-BREAK-AFTER = avoid to work\n        if (isset($this->blk[$this->blklvl])) {\n            $bottom = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['margin_bottom'];\n        } else {\n            $bottom = 0;\n        }\n\n        if (!$this->tableLevel\n            && (\n                ($this->y + $this->divheight > $this->PageBreakTrigger)\n                || ($this->y + $h > $this->PageBreakTrigger)\n                || (\n                    $this->y + ($h * 2) + $bottom > $this->PageBreakTrigger\n                    && $this->blk[$this->blklvl]['page_break_after_avoid']\n                )\n            )\n            && !$this->InFooter\n            && $this->AcceptPageBreak()\n        ) { // mPDF 5.7.2\n\n            $x = $this->x; // Current X position\n\n            // WORD SPACING\n            $ws = $this->ws; // Word Spacing\n            $charspacing = $this->charspacing; // Character Spacing\n            $this->ResetSpacing();\n\n            $this->AddPage($this->CurOrientation);\n\n            // Added to correct for OddEven Margins\n            $x += $this->MarginCorrection;\n            if ($currentx) {\n                $currentx += $this->MarginCorrection;\n            }\n            $this->x = $x;\n            // WORD SPACING\n            $this->SetSpacing($charspacing, $ws);\n        }\n\n        // Test: to put line through centre of cell: $this->Line($this->x,$this->y+($h/2),$this->x+50,$this->y+($h/2));\n        // Test: to put border around cell as it is specified: $border='LRTB';\n\n        /* -- COLUMNS -- */\n        // COLS\n        // COLUMN CHANGE\n        if ($this->CurrCol != $oldcolumn) {\n            if ($currentx) {\n                $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n            }\n            $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n        }\n\n        // COLUMNS Update/overwrite the lowest bottom of printing y value for a column\n        if ($this->ColActive) {\n            if ($h) {\n                $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;\n            } else {\n                $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $this->divheight;\n            }\n        }\n        /* -- END COLUMNS -- */\n\n\n        if ($w == 0) {\n            $w = $this->w - $this->rMargin - $this->x;\n        }\n\n        $s = '';\n        if ($fill == 1 && $this->FillColor) {\n            if ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor'])) {\n                $s .= $this->FillColor . ' ';\n            }\n            $this->pageoutput[$this->page]['FillColor'] = $this->FillColor;\n        }\n\n        if ($lineBox && isset($lineBox['boxtop']) && $txt) { // i.e. always from WriteFlowingBlock/finishFlowingBlock (but not objects -\n            // which only have $lineBox['top'] set)\n            $boxtop = $this->y + $lineBox['boxtop'];\n            $boxbottom = $this->y + $lineBox['boxbottom'];\n            $glyphYorigin = $lineBox['glyphYorigin'];\n            $baseline_shift = $lineBox['baseline-shift'];\n            $bord_boxtop = $bg_boxtop = $boxtop = $boxtop - $baseline_shift;\n            $bord_boxbottom = $bg_boxbottom = $boxbottom = $boxbottom - $baseline_shift;\n            $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;\n\n            // If inline element BACKGROUND has bounding box set by parent element:\n            if (isset($lineBox['background-boxtop'])) {\n                $bg_boxtop = $this->y + $lineBox['background-boxtop'] - $lineBox['background-baseline-shift'];\n                $bg_boxbottom = $this->y + $lineBox['background-boxbottom'] - $lineBox['background-baseline-shift'];\n                $bg_boxheight = $bg_boxbottom - $bg_boxtop;\n            }\n            // If inline element BORDER has bounding box set by parent element:\n            if (isset($lineBox['border-boxtop'])) {\n                $bord_boxtop = $this->y + $lineBox['border-boxtop'] - $lineBox['border-baseline-shift'];\n                $bord_boxbottom = $this->y + $lineBox['border-boxbottom'] - $lineBox['border-baseline-shift'];\n                $bord_boxheight = $bord_boxbottom - $bord_boxtop;\n            }\n\n        } else {\n\n            $boxtop = $this->y;\n            $boxheight = $h;\n            $boxbottom = $this->y + $h;\n            $baseline_shift = 0;\n\n            if ($txt != '') {\n\n                // FONT SIZE - this determines the baseline caculation\n                $bfs = $this->FontSize;\n                // Calculate baseline Superscript and Subscript Y coordinate adjustment\n                $bfx = $this->baselineC;\n                $baseline = $bfx * $bfs;\n\n                if ($textvar & TextVars::FA_SUPERSCRIPT) {\n                    $baseline_shift = $this->textparam['text-baseline'];\n                } elseif ($textvar & TextVars::FA_SUBSCRIPT) {\n                    $baseline_shift = $this->textparam['text-baseline'];\n                } elseif ($this->bullet) {\n                    $baseline += ($bfx - 0.7) * $this->FontSize;\n                }\n\n                // Vertical align (for Images)\n                if ($valign == 'T') {\n                    $va = (0.5 * $bfs * $this->normalLineheight);\n                } elseif ($valign == 'B') {\n                    $va = $h - (0.5 * $bfs * $this->normalLineheight);\n                } else {\n                    $va = 0.5 * $h;\n                } // Middle\n\n                // ONLY SET THESE IF WANT TO CONFINE BORDER +/- FILL TO FIT FONTSIZE - NOT FULL CELL AS IS ORIGINAL FUNCTION\n                // spanfill or spanborder are set in FlowingBlock functions\n                if ($spanfill || !empty($this->spanborddet) || $link != '') {\n                    $exth = 0.2; // Add to fontsize to increase height of background / link / border\n                    $boxtop = $this->y + $baseline + $va - ($this->FontSize * (1 + $exth / 2) * (0.5 + $bfx));\n                    $boxheight = $this->FontSize * (1 + $exth);\n                    $boxbottom = $boxtop + $boxheight;\n                }\n\n                $glyphYorigin = $baseline + $va;\n            }\n\n            $boxtop -= $baseline_shift;\n            $boxbottom -= $baseline_shift;\n            $bord_boxtop = $bg_boxtop = $boxtop;\n            $bord_boxbottom = $bg_boxbottom = $boxbottom;\n            $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;\n        }\n\n        $bbw = $tbw = $lbw = $rbw = 0; // Border widths\n        if (!empty($this->spanborddet)) {\n\n            if (!isset($this->spanborddet['B'])) {\n                $this->spanborddet['B'] = ['s' => 0, 'style' => '', 'w' => 0];\n            }\n\n            if (!isset($this->spanborddet['T'])) {\n                $this->spanborddet['T'] = ['s' => 0, 'style' => '', 'w' => 0];\n            }\n\n            if (!isset($this->spanborddet['L'])) {\n                $this->spanborddet['L'] = ['s' => 0, 'style' => '', 'w' => 0];\n            }\n\n            if (!isset($this->spanborddet['R'])) {\n                $this->spanborddet['R'] = ['s' => 0, 'style' => '', 'w' => 0];\n            }\n\n            $bbw = $this->spanborddet['B']['w'];\n            $tbw = $this->spanborddet['T']['w'];\n            $lbw = $this->spanborddet['L']['w'];\n            $rbw = $this->spanborddet['R']['w'];\n        }\n\n        if ($fill == 1 || $border == 1 || !empty($this->spanborddet)) {\n\n            if (!empty($this->spanborddet)) {\n\n                if ($fill == 1) {\n                    $s .= sprintf('%.3F %.3F %.3F %.3F re f ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bg_boxtop + $tbw) * Mpdf::SCALE, ($w + $lbw + $rbw) * Mpdf::SCALE, (-$bg_boxheight - $tbw - $bbw) * Mpdf::SCALE);\n                }\n\n                $s.= ' q ';\n                $dashon = 3;\n                $dashoff = 3.5;\n                $dot = 2.5;\n\n                if ($tbw) {\n                    $short = 0;\n\n                    if ($this->spanborddet['T']['style'] == 'dashed') {\n                        $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $tbw * $dashon * Mpdf::SCALE, $tbw * $dashoff * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['T']['style'] == 'dotted') {\n                        $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $tbw * $dot * Mpdf::SCALE, -$tbw / 2 * Mpdf::SCALE);\n                        $short = $tbw / 2;\n                    } else {\n                        $s .= ' 0 j 0 J [] 0 d ';\n                    }\n\n                    if ($this->spanborddet['T']['style'] != 'dotted') {\n                        $s .= 'q ';\n                        $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);\n                        $s .= ' h W n '; // Ends path no-op & Sets the clipping path\n                    }\n\n                    $c = $this->SetDColor($this->spanborddet['T']['c'], true);\n\n                    if ($this->spanborddet['T']['style'] == 'double') {\n                        $s .= sprintf(' %s %.3F w ', $c, $tbw / 3 * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw * 5 / 6) * Mpdf::SCALE, ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw * 5 / 6) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 6) * Mpdf::SCALE, ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 6) * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['T']['style'] == 'dotted') {\n                        $s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE);\n                    } else {\n                        $s .= sprintf(' %s %.3F w ', $c, $tbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw / 2) * Mpdf::SCALE);\n                    }\n\n                    if ($this->spanborddet['T']['style'] != 'dotted') {\n                        $s .= ' Q ';\n                    }\n                }\n                if ($bbw) {\n\n                    $short = 0;\n                    if ($this->spanborddet['B']['style'] == 'dashed') {\n                        $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $bbw * $dashon * Mpdf::SCALE, $bbw * $dashoff * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['B']['style'] == 'dotted') {\n                        $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $bbw * $dot * Mpdf::SCALE, -$bbw / 2 * Mpdf::SCALE);\n                        $short = $bbw / 2;\n                    } else {\n                        $s .= ' 0 j 0 J [] 0 d ';\n                    }\n\n                    if ($this->spanborddet['B']['style'] != 'dotted') {\n                        $s .= 'q ';\n                        $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);\n                        $s .= ' h W n '; // Ends path no-op & Sets the clipping path\n                    }\n\n                    $c = $this->SetDColor($this->spanborddet['B']['c'], true);\n\n                    if ($this->spanborddet['B']['style'] == 'double') {\n                        $s .= sprintf(' %s %.3F w ', $c, $bbw / 3 * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 6) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 6) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['B']['style'] == 'dotted') {\n                        $s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE);\n                    } else {\n                        $s .= sprintf(' %s %.3F w ', $c, $bbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE, ($this->x + $w + $rbw - $short) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw / 2) * Mpdf::SCALE);\n                    }\n\n                    if ($this->spanborddet['B']['style'] != 'dotted') {\n                        $s .= ' Q ';\n                    }\n                }\n\n                if ($lbw) {\n                    $short = 0;\n                    if ($this->spanborddet['L']['style'] == 'dashed') {\n                        $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $lbw * $dashon * Mpdf::SCALE, $lbw * $dashoff * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['L']['style'] == 'dotted') {\n                        $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $lbw * $dot * Mpdf::SCALE, -$lbw / 2 * Mpdf::SCALE);\n                        $short = $lbw / 2;\n                    } else {\n                        $s .= ' 0 j 0 J [] 0 d ';\n                    }\n\n                    if ($this->spanborddet['L']['style'] != 'dotted') {\n                        $s .= 'q ';\n                        $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x - $lbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);\n                        $s .= ' h W n '; // Ends path no-op & Sets the clipping path\n                    }\n\n                    $c = $this->SetDColor($this->spanborddet['L']['c'], true);\n                    if ($this->spanborddet['L']['style'] == 'double') {\n                        $s .= sprintf(' %s %.3F w ', $c, $lbw / 3 * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['L']['style'] == 'dotted') {\n                        $s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                    } else {\n                        $s .= sprintf(' %s %.3F w ', $c, $lbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x - $lbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                    }\n\n                    if ($this->spanborddet['L']['style'] != 'dotted') {\n                        $s .= ' Q ';\n                    }\n                }\n\n                if ($rbw) {\n\n                    $short = 0;\n                    if ($this->spanborddet['R']['style'] == 'dashed') {\n                        $s .= sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $rbw * $dashon * Mpdf::SCALE, $rbw * $dashoff * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['R']['style'] == 'dotted') {\n                        $s .= sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $rbw * $dot * Mpdf::SCALE, -$rbw / 2 * Mpdf::SCALE);\n                        $short = $rbw / 2;\n                    } else {\n                        $s .= ' 0 j 0 J [] 0 d ';\n                    }\n\n                    if ($this->spanborddet['R']['style'] != 'dotted') {\n                        $s .= 'q ';\n                        $s .= sprintf('%.3F %.3F m ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxbottom) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE);\n                        $s .= ' h W n '; // Ends path no-op & Sets the clipping path\n                    }\n\n                    $c = $this->SetDColor($this->spanborddet['R']['c'], true);\n                    if ($this->spanborddet['R']['style'] == 'double') {\n                        $s .= sprintf(' %s %.3F w ', $c, $rbw / 3 * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw * 5 / 6) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                    } elseif ($this->spanborddet['R']['style'] == 'dotted') {\n                        $s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                    } else {\n                        $s .= sprintf(' %s %.3F w ', $c, $rbw * Mpdf::SCALE);\n                        $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxtop + $tbw) * Mpdf::SCALE, ($this->x + $w + $rbw / 2) * Mpdf::SCALE, ($this->h - $bord_boxbottom - $bbw + $short) * Mpdf::SCALE);\n                    }\n\n                    if ($this->spanborddet['R']['style'] != 'dotted') {\n                        $s .= ' Q ';\n                    }\n                }\n\n                $s.= ' Q ';\n\n            } else { // If \"border\", does not come from WriteFlowingBlock or FinishFlowingBlock\n\n                if ($fill == 1) {\n                    $op = ($border == 1) ? 'B' : 'f';\n                } else {\n                    $op = 'S';\n                }\n\n                $s .= sprintf('%.3F %.3F %.3F %.3F re %s ', $this->x * Mpdf::SCALE, ($this->h - $bg_boxtop) * Mpdf::SCALE, $w * Mpdf::SCALE, -$bg_boxheight * Mpdf::SCALE, $op);\n            }\n        }\n\n        if (is_string($border)) { // If \"border\", does not come from WriteFlowingBlock or FinishFlowingBlock\n\n            $x = $this->x;\n            $y = $this->y;\n\n            if (is_int(strpos($border, 'L'))) {\n                $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE);\n            }\n\n            if (is_int(strpos($border, 'T'))) {\n                $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE);\n            }\n\n            if (is_int(strpos($border, 'R'))) {\n                $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', ($x + $w) * Mpdf::SCALE, ($this->h - $bord_boxtop) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE);\n            }\n\n            if (is_int(strpos($border, 'B'))) {\n                $s .= sprintf('%.3F %.3F m %.3F %.3F l S ', $x * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE, ($x + $w) * Mpdf::SCALE, ($this->h - ($bord_boxbottom)) * Mpdf::SCALE);\n            }\n        }\n\n        if ($txt != '') {\n\n            if ($exactWidth) {\n                $stringWidth = $w;\n            } else {\n                $stringWidth = $this->GetStringWidth($txt, true, $OTLdata, $textvar) + ( $this->charspacing * mb_strlen($txt, $this->mb_enc) / Mpdf::SCALE ) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc) / Mpdf::SCALE );\n            }\n\n            // Set x OFFSET FOR PRINTING\n            if ($align == 'R') {\n                $dx = $w - $this->cMarginR - $stringWidth - $lcpaddingR;\n            } elseif ($align == 'C') {\n                $dx = (($w - $stringWidth ) / 2);\n            } elseif ($align == 'L' or $align == 'J') {\n                $dx = $this->cMarginL + $lcpaddingL;\n            } else {\n                $dx = 0;\n            }\n\n            if ($this->ColorFlag) {\n                $s .='q ' . $this->TextColor . ' ';\n            }\n\n            // OUTLINE\n            if (isset($this->textparam['outline-s']) && $this->textparam['outline-s'] && !($textvar & TextVars::FC_SMALLCAPS)) { // mPDF 5.7.1\n                $s .=' ' . sprintf('%.3F w', $this->LineWidth * Mpdf::SCALE) . ' ';\n                $s .=\" $this->DrawColor \";\n                $s .=\" 2 Tr \";\n            } elseif ($this->falseBoldWeight && strpos($this->ReqFontStyle, \"B\") !== false && strpos($this->FontStyle, \"B\") === false && !($textvar & TextVars::FC_SMALLCAPS)) { // can't use together with OUTLINE or Small Caps\t// mPDF 5.7.1\t??? why not with SmallCaps ???\n                $s .= ' 2 Tr 1 J 1 j ';\n                $s .= ' ' . sprintf('%.3F w', ($this->FontSize / 130) * Mpdf::SCALE * $this->falseBoldWeight) . ' ';\n                $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG\n                if ($this->FillColor != $tc) {\n                    $s .= ' ' . $tc . ' ';\n                }  // stroke (outline) = same colour as text(fill)\n            } else {\n                $s .=\" 0 Tr \";\n            }\n\n            if (strpos($this->ReqFontStyle, \"I\") !== false && strpos($this->FontStyle, \"I\") === false) { // Artificial italic\n                $aix = '1 0 0.261799 1 %.3F %.3F Tm ';\n            } else {\n                $aix = '%.3F %.3F Td ';\n            }\n\n            $px = ($this->x + $dx) * Mpdf::SCALE;\n            $py = ($this->h - ($this->y + $glyphYorigin - $baseline_shift)) * Mpdf::SCALE;\n\n            // THE TEXT\n            $txt2 = $txt;\n            $sub = '';\n            $this->CurrentFont['used'] = true;\n\n            /*             * ************** SIMILAR TO Text() ************************ */\n\n            // IF corefonts AND NOT SmCaps AND NOT Kerning\n            // Just output text; charspacing and wordspacing already set by charspacing (Tc) and ws (Tw)\n            if ($this->usingCoreFont && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING)) {\n                $txt2 = $this->writer->escape($txt2);\n                $sub .= sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);\n            } // IF NOT corefonts AND NO wordspacing AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL\n            // Just output text\n            elseif (!$this->usingCoreFont && !$this->ws && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {\n                // IF SIP/SMP\n                if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {\n                    $txt2 = $this->UTF8toSubset($txt2);\n                    $sub .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);\n                } // NOT SIP/SMP\n                else {\n                    $txt2 = $this->writer->utf8ToUtf16BigEndian($txt2, false);\n                    $txt2 = $this->writer->escape($txt2);\n                    $sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);\n                }\n            } // IF NOT corefonts AND IS wordspacing AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL\n            // Output text word by word with an adjustment to the intercharacter spacing for SPACEs to form word spacing\n            // IF multibyte - Tw has no effect - need to do word spacing using an adjustment before each space\n            elseif (!$this->usingCoreFont && $this->ws && !((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) && !($textvar & TextVars::FC_SMALLCAPS) && !($textvar & TextVars::FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && (!empty($OTLdata['GPOSinfo']) || (strpos($OTLdata['group'], 'M') !== false && $this->charspacing)) )) {\n                $space = \" \";\n                $space = $this->writer->utf8ToUtf16BigEndian($space, false);\n                $space = $this->writer->escape($space);\n                $sub .=sprintf('BT ' . $aix . ' %.3F Tc [', $px, $py, $this->charspacing);\n                $t = explode(' ', $txt2);\n                $numt = count($t);\n                for ($i = 0; $i < $numt; $i++) {\n                    $tx = $t[$i];\n                    $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);\n                    $tx = $this->writer->escape($tx);\n                    $sub .=sprintf('(%s) ', $tx);\n                    if (($i + 1) < $numt) {\n                        $adj = -($this->ws) * 1000 / $this->FontSizePt;\n                        $sub .=sprintf('%d(%s) ', $adj, $space);\n                    }\n                }\n                $sub .='] TJ ';\n                $sub .=' ET';\n            } // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]\n            else {\n                $sub = $this->applyGPOSpdf($txt, $aix, $px, $py, $OTLdata, $textvar);\n            }\n\n            /** ************** END SIMILAR TO Text() ************************ */\n\n            if ($this->shrin_k > 1) {\n                $shrin_k = $this->shrin_k;\n            } else {\n                $shrin_k = 1;\n            }\n\n            // UNDERLINE\n            if ($textvar & TextVars::FD_UNDERLINE) { // mPDF 5.7.1\t// mPDF 6\n\n                // mPDF 5.7.3  inline text-decoration parameters\n\n                $c = isset($this->textparam['u-decoration']['color']) ? $this->textparam['u-decoration']['color'] : '';\n                if ($this->FillColor != $c) {\n                    $sub .= ' ' . $c . ' ';\n                }\n\n                // mPDF 5.7.3  inline text-decoration parameters\n                $decorationfontkey = isset($this->textparam['u-decoration']['fontkey']) ? $this->textparam['u-decoration']['fontkey'] : '';\n                $decorationfontsize = isset($this->textparam['u-decoration']['fontsize']) ? $this->textparam['u-decoration']['fontsize'] / $shrin_k : 0;\n\n                if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {\n                    $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;\n                } else {\n                    $ut = 60 / 1000 * $decorationfontsize;\n                }\n\n                if (isset($this->fonts[$decorationfontkey]['up']) && $this->fonts[$decorationfontkey]['up']) {\n                    $up = $this->fonts[$decorationfontkey]['up'];\n                } else {\n                    $up = -100;\n                }\n\n                $adjusty = (-$up / 1000 * $decorationfontsize) + $ut / 2;\n                $ubaseline = isset($this->textparam['u-decoration']['baseline'])\n                    ? $glyphYorigin - $this->textparam['u-decoration']['baseline'] / $shrin_k\n                    : $glyphYorigin;\n\n                $olw = $this->LineWidth;\n\n                $sub .= ' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));\n                $sub .= ' ' . $this->_dounderline($this->x + $dx, $this->y + $ubaseline + $adjusty, $txt, $OTLdata, $textvar);\n                $sub .= ' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));\n\n                if ($this->FillColor != $c) {\n                    $sub .= ' ' . $this->FillColor . ' ';\n                }\n            }\n\n            // STRIKETHROUGH\n            if ($textvar & TextVars::FD_LINETHROUGH) { // mPDF 5.7.1\t// mPDF 6\n\n                // mPDF 5.7.3  inline text-decoration parameters\n                $c = $this->textparam['s-decoration']['color'];\n\n                if ($this->FillColor != $c) {\n                    $sub .= ' ' . $c . ' ';\n                }\n\n                // mPDF 5.7.3  inline text-decoration parameters\n                $decorationfontkey = $this->textparam['s-decoration']['fontkey'];\n                $decorationfontsize = $this->textparam['s-decoration']['fontsize'] / $shrin_k;\n\n                // Use yStrikeoutSize from OS/2 if available\n                if (isset($this->fonts[$decorationfontkey]['strs']) && $this->fonts[$decorationfontkey]['strs']) {\n                    $ut = $this->fonts[$decorationfontkey]['strs'] / 1000 * $decorationfontsize;\n                } // else use underlineThickness from post if available\n                elseif (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {\n                    $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;\n                } else {\n                    $ut = 50 / 1000 * $decorationfontsize;\n                }\n\n                // Use yStrikeoutPosition from OS/2 if available\n                if (isset($this->fonts[$decorationfontkey]['strp']) && $this->fonts[$decorationfontkey]['strp']) {\n                    $up = $this->fonts[$decorationfontkey]['strp'];\n                    $adjusty = (-$up / 1000 * $decorationfontsize);\n                } // else use a fraction ($this->baselineS) of CapHeight\n                else {\n                    if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {\n                        $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];\n                    } else {\n                        $ch = 700;\n                    }\n                    $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineS;\n                }\n\n                $sbaseline = $glyphYorigin - $this->textparam['s-decoration']['baseline'] / $shrin_k;\n\n                $olw = $this->LineWidth;\n\n                $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));\n                $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $sbaseline + $adjusty, $txt, $OTLdata, $textvar);\n                $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));\n\n                if ($this->FillColor != $c) {\n                    $sub .= ' ' . $this->FillColor . ' ';\n                }\n            }\n\n            // mPDF 5.7.3  inline text-decoration parameters\n            // OVERLINE\n            if ($textvar & TextVars::FD_OVERLINE) { // mPDF 5.7.1\t// mPDF 6\n                // mPDF 5.7.3  inline text-decoration parameters\n                $c = $this->textparam['o-decoration']['color'];\n                if ($this->FillColor != $c) {\n                    $sub .= ' ' . $c . ' ';\n                }\n\n                // mPDF 5.7.3  inline text-decoration parameters\n                $decorationfontkey = (int) (((float) $this->textparam['o-decoration']['fontkey']) / $shrin_k);\n                $decorationfontsize = $this->textparam['o-decoration']['fontsize'];\n\n                if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {\n                    $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;\n                } else {\n                    $ut = 60 / 1000 * $decorationfontsize;\n                }\n                if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {\n                    $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];\n                } else {\n                    $ch = 700;\n                }\n                $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineO;\n                $obaseline = $glyphYorigin - $this->textparam['o-decoration']['baseline'] / $shrin_k;\n                $olw = $this->LineWidth;\n                $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * Mpdf::SCALE));\n                $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $obaseline + $adjusty, $txt, $OTLdata, $textvar);\n                $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * Mpdf::SCALE));\n                if ($this->FillColor != $c) {\n                    $sub .= ' ' . $this->FillColor . ' ';\n                }\n            }\n\n            // TEXT SHADOW\n            if ($this->textshadow) {  // First to process is last in CSS comma separated shadows\n                foreach ($this->textshadow as $ts) {\n                    $s .= ' q ';\n                    $s .= $this->SetTColor($ts['col'], true) . \"\\n\";\n                    if ($ts['col'][0] == 5 && ord($ts['col'][4]) < 100) { // RGBa\n                        $s .= $this->SetAlpha(ord($ts['col'][4]) / 100, 'Normal', true, 'F') . \"\\n\";\n                    } elseif ($ts['col'][0] == 6 && ord($ts['col'][5]) < 100) { // CMYKa\n                        $s .= $this->SetAlpha(ord($ts['col'][5]) / 100, 'Normal', true, 'F') . \"\\n\";\n                    } elseif ($ts['col'][0] == 1 && $ts['col'][2] == 1 && ord($ts['col'][3]) < 100) { // Gray\n                        $s .= $this->SetAlpha(ord($ts['col'][3]) / 100, 'Normal', true, 'F') . \"\\n\";\n                    }\n                    $s .= sprintf(' 1 0 0 1 %.4F %.4F cm', $ts['x'] * Mpdf::SCALE, -$ts['y'] * Mpdf::SCALE) . \"\\n\";\n                    $s .= $sub;\n                    $s .= ' Q ';\n                }\n            }\n\n            $s .= $sub;\n\n            // COLOR\n            if ($this->ColorFlag) {\n                $s .=' Q';\n            }\n\n            // LINK\n            if ($link != '') {\n                $this->Link($this->x, $boxtop, $w, $boxheight, $link);\n            }\n        }\n        if ($s) {\n            $this->writer->write($s);\n        }\n\n        // WORD SPACING\n        if ($this->ws && !$this->usingCoreFont) {\n            $this->writer->write(sprintf('BT %.3F Tc ET', $this->charspacing));\n        }\n        $this->lasth = $h;\n        if (strpos($txt, \"\\n\") !== false) {\n            $ln = 1; // cell recognizes \\n from <BR> tag\n        }\n        if ($ln > 0) {\n            // Go to next line\n            $this->y += $h;\n            if ($ln == 1) {\n                // Move to next line\n                if ($currentx != 0) {\n                    $this->x = $currentx;\n                } else {\n                    $this->x = $this->lMargin;\n                }\n            }\n        } else {\n            $this->x+=$w;\n        }\n    }\n\n    function applyGPOSpdf($txt, $aix, $x, $y, $OTLdata, $textvar = 0)\n    {\n        // Generate PDF string\n        // ==============================\n        if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {\n            $sipset = true;\n        } else {\n            $sipset = false;\n        }\n\n        if ($textvar & TextVars::FC_SMALLCAPS) {\n            $smcaps = true;\n        } // IF SmallCaps using transformation, NOT OTL\n        else {\n            $smcaps = false;\n        }\n\n        if ($sipset) {\n            $fontid = $last_fontid = $original_fontid = $this->CurrentFont['subsetfontids'][0];\n        } else {\n            $fontid = $last_fontid = $original_fontid = $this->CurrentFont['i'];\n        }\n        $SmallCapsON = false;  // state: uppercase/not\n        $lastSmallCapsON = false; // state: uppercase/not\n        $last_fontsize = $fontsize = $this->FontSizePt;\n        $last_fontstretch = $fontstretch = 100;\n        $groupBreak = false;\n\n        $unicode = $this->UTF8StringToArray($txt);\n\n        $GPOSinfo = (isset($OTLdata['GPOSinfo']) ? $OTLdata['GPOSinfo'] : []);\n        $charspacing = ($this->charspacing * 1000 / $this->FontSizePt);\n        $wordspacing = ($this->ws * 1000 / $this->FontSizePt);\n\n        $XshiftBefore = 0;\n        $XshiftAfter = 0;\n        $lastYPlacement = 0;\n\n        if ($sipset) {\n            // mPDF 6  DELETED ********\n            // \t$txt= preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $txt);\t// ? Need to adjust OTL info\n            // \t$txt= preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $txt);\t// ? Need to adjust OTL info\n            $tj = '<';\n        } else {\n            $tj = '(';\n        }\n\n        for ($i = 0; $i < count($unicode); $i++) {\n            $c = $unicode[$i];\n            $tx = '';\n            $XshiftBefore = $XshiftAfter;\n            $XshiftAfter = 0;\n            $YPlacement = 0;\n            $groupBreak = false;\n            $kashida = 0;\n            if (!empty($OTLdata)) {\n                // YPlacement from GPOS\n                if (isset($GPOSinfo[$i]['YPlacement']) && $GPOSinfo[$i]['YPlacement']) {\n                    $YPlacement = $GPOSinfo[$i]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];\n                    $groupBreak = true;\n                }\n                // XPlacement from GPOS\n                if (isset($GPOSinfo[$i]['XPlacement']) && $GPOSinfo[$i]['XPlacement']) {\n                    if (!isset($GPOSinfo[$i]['wDir']) || $GPOSinfo[$i]['wDir'] != 'RTL') {\n                        if (isset($GPOSinfo[$i]['BaseWidth'])) {\n                            $GPOSinfo[$i]['XPlacement'] -= $GPOSinfo[$i]['BaseWidth'];\n                        }\n                    }\n\n                    // Convert to PDF Text space (thousandths of a unit );\n                    $XshiftBefore += $GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                    $XshiftAfter += -$GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                }\n\n                // Kashida from GPOS\n                // Kashida is set as an absolute length value, but to adjust text needs to be converted to\n                // font-related size\n                if (isset($GPOSinfo[$i]['kashida_space']) && $GPOSinfo[$i]['kashida_space']) {\n                    $kashida = $GPOSinfo[$i]['kashida_space'];\n                }\n\n                if ($c == 32) { // word spacing\n                    $XshiftAfter += $wordspacing;\n                }\n\n                if (substr($OTLdata['group'], ($i + 1), 1) != 'M') { // Don't add inter-character spacing before Marks\n                    $XshiftAfter += $charspacing;\n                }\n\n                // ...applyGPOSpdf...\n                // XAdvance from GPOS - Convert to PDF Text space (thousandths of a unit );\n                if (((isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] != 'RTL') || !isset($GPOSinfo[$i]['wDir'])) && isset($GPOSinfo[$i]['XAdvanceL']) && $GPOSinfo[$i]['XAdvanceL']) {\n                    $XshiftAfter += $GPOSinfo[$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                } elseif (isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] == 'RTL' && isset($GPOSinfo[$i]['XAdvanceR']) && $GPOSinfo[$i]['XAdvanceR']) {\n                    $XshiftAfter += $GPOSinfo[$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                }\n            } // Character & Word spacing - if NOT OTL\n            else {\n                $XshiftAfter += $charspacing;\n                if ($c == 32) {\n                    $XshiftAfter += $wordspacing;\n                }\n            }\n\n            // IF Kerning done using pairs rather than OTL\n            if ($textvar & TextVars::FC_KERNING) {\n                if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {\n                    $XshiftBefore += $this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];\n                }\n            }\n\n            if ($YPlacement != $lastYPlacement) {\n                $groupBreak = true;\n            }\n\n            if ($XshiftBefore) {  // +ve value in PDF moves to the left\n                // If Fontstretch is ongoing, need to adjust X adjustments because these will be stretched out.\n                $XshiftBefore *= 100 / $last_fontstretch;\n                if ($sipset) {\n                    $tj .= sprintf('>%d<', (-$XshiftBefore));\n                } else {\n                    $tj .= sprintf(')%d(', (-$XshiftBefore));\n                }\n            }\n\n            // Small-Caps\n            if ($smcaps) {\n                if (isset($this->upperCase[$c])) {\n                    $c = $this->upperCase[$c];\n                    // $this->CurrentFont['subset'][$this->upperCase[$c]] = $this->upperCase[$c];\t// add the CAP to subset\n                    $SmallCapsON = true;\n                    // For $sipset\n                    if (!$lastSmallCapsON) {   // Turn ON SmallCaps\n                        $groupBreak = true;\n                        $fontstretch = $this->smCapsStretch;\n                        $fontsize = $this->FontSizePt * $this->smCapsScale;\n                    }\n                } else {\n                    $SmallCapsON = false;\n                    if ($lastSmallCapsON) {  // Turn OFF SmallCaps\n                        $groupBreak = true;\n                        $fontstretch = 100;\n                        $fontsize = $this->FontSizePt;\n                    }\n                }\n            }\n\n            // Prepare Text and Select Font ID\n            if ($sipset) {\n                // mPDF 6  DELETED ********\n                // if ($c == 7 || $c == 8) {\n                // if ($original_fontid != $last_fontid) {\n                // \t$groupBreak = true;\n                // \t$fontid = $original_fontid;\n                // }\n                // if ($c == 7) { $tj .= $this->aliasNbPgHex; }\n                // else { $tj .= $this->aliasNbPgGpHex; }\n                // continue;\n                // }\n                for ($j = 0; $j < 99; $j++) {\n                    $init = array_search($c, $this->CurrentFont['subsets'][$j]);\n                    if ($init !== false) {\n                        if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {\n                            $groupBreak = true;\n                            $fontid = $this->CurrentFont['subsetfontids'][$j];\n                        }\n                        $tx = sprintf(\"%02s\", strtoupper(dechex($init)));\n                        break;\n                    } elseif (count($this->CurrentFont['subsets'][$j]) < 255) {\n                        $n = count($this->CurrentFont['subsets'][$j]);\n                        $this->CurrentFont['subsets'][$j][$n] = $c;\n                        if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {\n                            $groupBreak = true;\n                            $fontid = $this->CurrentFont['subsetfontids'][$j];\n                        }\n                        $tx = sprintf(\"%02s\", strtoupper(dechex($n)));\n                        break;\n                    } elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {\n                        $this->CurrentFont['subsets'][($j + 1)] = [0 => 0];\n                        $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;\n                        $this->extraFontSubsets++;\n                    }\n                }\n            } else {\n                $tx = UtfString::code2utf($c);\n                if ($this->usingCoreFont) {\n                    $tx = utf8_decode($tx);\n                } else {\n                    $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);\n                }\n                $tx = $this->writer->escape($tx);\n            }\n\n            // If any settings require a new Text Group\n            if ($groupBreak || $fontstretch != $last_fontstretch) {\n                if ($sipset) {\n                    $tj .= '>] TJ ';\n                } else {\n                    $tj .= ')] TJ ';\n                }\n                if ($fontid != $last_fontid || $fontsize != $last_fontsize) {\n                    $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);\n                }\n                if ($fontstretch != $last_fontstretch) {\n                    $tj .= sprintf('%d Tz ', $fontstretch);\n                }\n                if ($YPlacement != $lastYPlacement) {\n                    $tj .= sprintf('%.3F Ts ', $YPlacement);\n                }\n                if ($sipset) {\n                    $tj .= '[<';\n                } else {\n                    $tj .= '[(';\n                }\n            }\n\n            // Output the code for the txt character\n            $tj .= $tx;\n            $lastSmallCapsON = $SmallCapsON;\n            $last_fontid = $fontid;\n            $last_fontsize = $fontsize;\n            $last_fontstretch = $fontstretch;\n\n            // Kashida\n            if ($kashida) {\n                $c = 0x0640; // add the Tatweel U+0640\n                if (isset($this->CurrentFont['subset'])) {\n                    $this->CurrentFont['subset'][$c] = $c;\n                }\n                $kashida *= 1000 / $this->FontSizePt;\n                $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);\n\n                // Get YPlacement from next Base character\n                $nextbase = $i + 1;\n                while ($OTLdata['group'][$nextbase] != 'C') {\n                    $nextbase++;\n                }\n                if (isset($GPOSinfo[$nextbase]) && isset($GPOSinfo[$nextbase]['YPlacement']) && $GPOSinfo[$nextbase]['YPlacement']) {\n                    $YPlacement = $GPOSinfo[$nextbase]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];\n                }\n\n                // Prepare Text and Select Font ID\n                if ($sipset) {\n                    for ($j = 0; $j < 99; $j++) {\n                        $init = array_search($c, $this->CurrentFont['subsets'][$j]);\n                        if ($init !== false) {\n                            if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {\n                                $fontid = $this->CurrentFont['subsetfontids'][$j];\n                            }\n                            $tx = sprintf(\"%02s\", strtoupper(dechex($init)));\n                            break;\n                        } elseif (count($this->CurrentFont['subsets'][$j]) < 255) {\n                            $n = count($this->CurrentFont['subsets'][$j]);\n                            $this->CurrentFont['subsets'][$j][$n] = $c;\n                            if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {\n                                $fontid = $this->CurrentFont['subsetfontids'][$j];\n                            }\n                            $tx = sprintf(\"%02s\", strtoupper(dechex($n)));\n                            break;\n                        } elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {\n                            $this->CurrentFont['subsets'][($j + 1)] = [0 => 0];\n                            $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;\n                            $this->extraFontSubsets++;\n                        }\n                    }\n                } else {\n                    $tx = UtfString::code2utf($c);\n                    $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);\n                    $tx = $this->writer->escape($tx);\n                }\n\n                if ($kashida > $tatw) {\n                    // Insert multiple tatweel characters, repositioning the last one to give correct total length\n                    $fontstretch = 100;\n                    $nt = intval($kashida / $tatw);\n                    $nudgeback = (($nt + 1) * $tatw) - $kashida;\n                    $optx = str_repeat($tx, $nt);\n                    if ($sipset) {\n                        $optx .= sprintf('>%d<', ($nudgeback));\n                    } else {\n                        $optx .= sprintf(')%d(', ($nudgeback));\n                    }\n                    $optx .= $tx; // #last\n                } else {\n                    // Insert single tatweel character and use fontstretch to get correct length\n                    $fontstretch = ($kashida / $tatw) * 100;\n                    $optx = $tx;\n                }\n\n                if ($sipset) {\n                    $tj .= '>] TJ ';\n                } else {\n                    $tj .= ')] TJ ';\n                }\n                if ($fontid != $last_fontid || $fontsize != $last_fontsize) {\n                    $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);\n                }\n                if ($fontstretch != $last_fontstretch) {\n                    $tj .= sprintf('%d Tz ', $fontstretch);\n                }\n                $tj .= sprintf('%.3F Ts ', $YPlacement);\n                if ($sipset) {\n                    $tj .= '[<';\n                } else {\n                    $tj .= '[(';\n                }\n\n                // Output the code for the txt character(s)\n                $tj .= $optx;\n                $last_fontid = $fontid;\n                $last_fontstretch = $fontstretch;\n                $fontstretch = 100;\n            }\n\n            $lastYPlacement = $YPlacement;\n        }\n\n\n        // Finish up\n        if ($sipset) {\n            $tj .= '>';\n            if ($XshiftAfter) {\n                $tj .= sprintf('%d', (-$XshiftAfter));\n            }\n            if ($last_fontid != $original_fontid) {\n                $tj .= '] TJ ';\n                $tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);\n                $tj .= '[';\n            }\n            $tj = preg_replace('/([^\\\\\\])<>/', '\\\\1 ', $tj);\n        } else {\n            $tj .= ')';\n            if ($XshiftAfter) {\n                $tj .= sprintf('%d', (-$XshiftAfter));\n            }\n            if ($last_fontid != $original_fontid) {\n                $tj .= '] TJ ';\n                $tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);\n                $tj .= '[';\n            }\n            $tj = preg_replace('/([^\\\\\\])\\(\\)/', '\\\\1 ', $tj);\n        }\n\n        $s = sprintf(' BT ' . $aix . ' 0 Tc 0 Tw [%s] TJ ET ', $x, $y, $tj);\n\n        // echo $s.\"\\n\\n\"; // exit;\n\n        return $s;\n    }\n\n    function _kern($txt, $mode, $aix, $x, $y)\n    {\n        if ($mode == 'MBTw') { // Multibyte requiring word spacing\n            $space = ' ';\n            // Convert string to UTF-16BE without BOM\n            $space = $this->writer->utf8ToUtf16BigEndian($space, false);\n            $space = $this->writer->escape($space);\n            $s = sprintf(' BT ' . $aix, $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE);\n            $t = explode(' ', $txt);\n            for ($i = 0; $i < count($t); $i++) {\n                $tx = $t[$i];\n\n                $tj = '(';\n                $unicode = $this->UTF8StringToArray($tx);\n                for ($ti = 0; $ti < count($unicode); $ti++) {\n                    if ($ti > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]])) {\n                        $kern = -$this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]];\n                        $tj .= sprintf(')%d(', $kern);\n                    }\n                    $tc = UtfString::code2utf($unicode[$ti]);\n                    $tc = $this->writer->utf8ToUtf16BigEndian($tc, false);\n                    $tj .= $this->writer->escape($tc);\n                }\n                $tj .= ')';\n                $s .= sprintf(' %.3F Tc [%s] TJ', $this->charspacing, $tj);\n\n\n                if (($i + 1) < count($t)) {\n                    $s .= sprintf(' %.3F Tc (%s) Tj', $this->ws + $this->charspacing, $space);\n                }\n            }\n            $s .= ' ET ';\n        } elseif (!$this->usingCoreFont) {\n            $s = '';\n            $tj = '(';\n            $unicode = $this->UTF8StringToArray($txt);\n            for ($i = 0; $i < count($unicode); $i++) {\n                if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {\n                    $kern = -$this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];\n                    $tj .= sprintf(')%d(', $kern);\n                }\n                $tx = UtfString::code2utf($unicode[$i]);\n                $tx = $this->writer->utf8ToUtf16BigEndian($tx, false);\n                $tj .= $this->writer->escape($tx);\n            }\n            $tj .= ')';\n            $s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);\n        } else { // CORE Font\n            $s = '';\n            $tj = '(';\n            $l = strlen($txt);\n            for ($i = 0; $i < $l; $i++) {\n                if ($i > 0 && isset($this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]])) {\n                    $kern = -$this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]];\n                    $tj .= sprintf(')%d(', $kern);\n                }\n                $tj .= $this->writer->escape($txt[$i]);\n            }\n            $tj .= ')';\n            $s .= sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $tj);\n        }\n\n        return $s;\n    }\n\n    function MultiCell($w, $h, $txt, $border = 0, $align = '', $fill = 0, $link = '', $directionality = 'ltr', $encoded = false, $OTLdata = false, $maxrows = false)\n    {\n        // maxrows is called from mpdfform->TEXTAREA\n        // Parameter (pre-)encoded - When called internally from form::textarea - mb_encoding already done and OTL - but not reverse RTL\n        if (!$encoded) {\n            $txt = $this->purify_utf8_text($txt);\n            if ($this->text_input_as_HTML) {\n                $txt = $this->all_entities_to_utf8($txt);\n            }\n            if ($this->usingCoreFont) {\n                $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');\n            }\n            if (preg_match(\"/([\" . $this->pregRTLchars . \"])/u\", $txt)) {\n                $this->biDirectional = true;\n            } // *OTL*\n            /* -- OTL -- */\n            $OTLdata = [];\n            // Use OTL OpenType Table Layout - GSUB & GPOS\n            if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);\n                $OTLdata = $this->otl->OTLdata;\n            }\n            if ($directionality == 'rtl' || $this->biDirectional) {\n                if (!isset($OTLdata)) {\n                    $unicode = $this->UTF8StringToArray($txt, false);\n                    $is_strong = false;\n                    $this->getBasicOTLdata($OTLdata, $unicode, $is_strong);\n                }\n            }\n            /* -- END OTL -- */\n        }\n        if (!$align) {\n            $align = $this->defaultAlign;\n        }\n\n        // Output text with automatic or explicit line breaks\n        $cw = &$this->CurrentFont['cw'];\n        if ($w == 0) {\n            $w = $this->w - $this->rMargin - $this->x;\n        }\n\n        $wmax = ($w - ($this->cMarginL + $this->cMarginR));\n        if ($this->usingCoreFont) {\n            $s = str_replace(\"\\r\", '', $txt);\n            $nb = strlen($s);\n            while ($nb > 0 and $s[$nb - 1] == \"\\n\") {\n                $nb--;\n            }\n        } else {\n            $s = str_replace(\"\\r\", '', $txt);\n            $nb = mb_strlen($s, $this->mb_enc);\n            while ($nb > 0 and mb_substr($s, $nb - 1, 1, $this->mb_enc) == \"\\n\") {\n                $nb--;\n            }\n        }\n        $b = 0;\n        if ($border) {\n            if ($border == 1) {\n                $border = 'LTRB';\n                $b = 'LRT';\n                $b2 = 'LR';\n            } else {\n                $b2 = '';\n                if (is_int(strpos($border, 'L'))) {\n                    $b2 .= 'L';\n                }\n                if (is_int(strpos($border, 'R'))) {\n                    $b2 .= 'R';\n                }\n                $b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2;\n            }\n        }\n        $sep = -1;\n        $i = 0;\n        $j = 0;\n        $l = 0;\n        $ns = 0;\n        $nl = 1;\n\n        $rows = 0;\n        $start_y = $this->y;\n\n        if (!$this->usingCoreFont) {\n            $inclCursive = false;\n            if (preg_match(\"/([\" . $this->pregCURSchars . \"])/u\", $s)) {\n                $inclCursive = true;\n            }\n            while ($i < $nb) {\n                // Get next character\n                $c = mb_substr($s, $i, 1, $this->mb_enc);\n                if ($c == \"\\n\") {\n                    // Explicit line break\n                    // WORD SPACING\n                    $this->ResetSpacing();\n                    $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));\n                    $tmpOTLdata = false;\n                    /* -- OTL -- */\n                    if (isset($OTLdata)) {\n                        $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);\n                        $this->otl->trimOTLdata($tmpOTLdata, false, true);\n                        $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);\n                    }\n                    /* -- END OTL -- */\n                    $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);\n                    if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {\n                        return false;\n                    }\n                    $i++;\n                    $sep = -1;\n                    $j = $i;\n                    $l = 0;\n                    $ns = 0;\n                    $nl++;\n                    if ($border and $nl == 2) {\n                        $b = $b2;\n                    }\n                    continue;\n                }\n                if ($c == \" \") {\n                    $sep = $i;\n                    $ls = $l;\n                    $ns++;\n                }\n\n                $l += $this->GetCharWidthNonCore($c);\n\n                if ($l > $wmax) {\n                    // Automatic line break\n                    if ($sep == -1) { // Only one word\n                        if ($i == $j) {\n                            $i++;\n                        }\n                        // WORD SPACING\n                        $this->ResetSpacing();\n                        $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));\n                        $tmpOTLdata = false;\n                        /* -- OTL -- */\n                        if (isset($OTLdata)) {\n                            $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);\n                            $this->otl->trimOTLdata($tmpOTLdata, false, true);\n                            $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);\n                        }\n                        /* -- END OTL -- */\n                        $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);\n                    } else {\n                        $tmp = rtrim(mb_substr($s, $j, $sep - $j, $this->mb_enc));\n                        $tmpOTLdata = false;\n                        /* -- OTL -- */\n                        if (isset($OTLdata)) {\n                            $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $sep - $j);\n                            $this->otl->trimOTLdata($tmpOTLdata, false, true);\n                        }\n                        /* -- END OTL -- */\n                        if ($align == 'J') {\n                            //////////////////////////////////////////\n                            // JUSTIFY J using Unicode fonts (Word spacing doesn't work)\n                            // WORD SPACING UNICODE\n                            // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly\n                            $tmp = str_replace(chr(194) . chr(160), chr(32), $tmp);\n                            $len_ligne = $this->GetStringWidth($tmp, false, $tmpOTLdata);\n                            $nb_carac = mb_strlen($tmp, $this->mb_enc);\n                            $nb_spaces = mb_substr_count($tmp, ' ', $this->mb_enc);\n                            // Take off number of Marks\n                            // Use GPOS OTL\n                            if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'])) {\n                                if (isset($tmpOTLdata['group']) && $tmpOTLdata['group']) {\n                                    $nb_carac -= substr_count($tmpOTLdata['group'], 'M');\n                                }\n                            }\n\n                            [$charspacing, $ws, $kashida] = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), $inclCursive, $tmpOTLdata);\n                            $this->SetSpacing($charspacing, $ws);\n                            //////////////////////////////////////////\n                        }\n                        if (isset($OTLdata)) {\n                            $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);\n                        }\n                        $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);\n                        $i = $sep + 1;\n                    }\n                    if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {\n                        return false;\n                    }\n                    $sep = -1;\n                    $j = $i;\n                    $l = 0;\n                    $ns = 0;\n                    $nl++;\n                    if ($border and $nl == 2) {\n                        $b = $b2;\n                    }\n                } else {\n                    $i++;\n                }\n            }\n            // Last chunk\n            // WORD SPACING\n\n            $this->ResetSpacing();\n        } else {\n            while ($i < $nb) {\n                // Get next character\n                $c = $s[$i];\n                if ($c == \"\\n\") {\n                    // Explicit line break\n                    // WORD SPACING\n                    $this->ResetSpacing();\n                    $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);\n                    if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {\n                        return false;\n                    }\n                    $i++;\n                    $sep = -1;\n                    $j = $i;\n                    $l = 0;\n                    $ns = 0;\n                    $nl++;\n                    if ($border and $nl == 2) {\n                        $b = $b2;\n                    }\n                    continue;\n                }\n                if ($c == \" \") {\n                    $sep = $i;\n                    $ls = $l;\n                    $ns++;\n                }\n\n                $l += $this->GetCharWidthCore($c);\n                if ($l > $wmax) {\n                    // Automatic line break\n                    if ($sep == -1) {\n                        if ($i == $j) {\n                            $i++;\n                        }\n                        // WORD SPACING\n                        $this->ResetSpacing();\n                        $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);\n                    } else {\n                        if ($align == 'J') {\n                            $tmp = rtrim(substr($s, $j, $sep - $j));\n                            //////////////////////////////////////////\n                            // JUSTIFY J using Unicode fonts (Word spacing doesn't work)\n                            // WORD SPACING NON_UNICODE/CJK\n                            // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly\n                            $tmp = str_replace(chr(160), chr(32), $tmp);\n                            $len_ligne = $this->GetStringWidth($tmp);\n                            $nb_carac = strlen($tmp);\n                            $nb_spaces = substr_count($tmp, ' ');\n                            $tmpOTLdata = [];\n                            [$charspacing, $ws, $kashida] = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * Mpdf::SCALE), false, $tmpOTLdata);\n                            $this->SetSpacing($charspacing, $ws);\n                            //////////////////////////////////////////\n                        }\n                        $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill, $link);\n                        $i = $sep + 1;\n                    }\n                    if ($maxrows != false && isset($this->form) && ($this->y - $start_y) / $h > $maxrows) {\n                        return false;\n                    }\n                    $sep = -1;\n                    $j = $i;\n                    $l = 0;\n                    $ns = 0;\n                    $nl++;\n                    if ($border and $nl == 2) {\n                        $b = $b2;\n                    }\n                } else {\n                    $i++;\n                }\n            }\n            // Last chunk\n            // WORD SPACING\n\n            $this->ResetSpacing();\n        }\n        // Last chunk\n        if ($border and is_int(strpos($border, 'B'))) {\n            $b .= 'B';\n        }\n        if (!$this->usingCoreFont) {\n            $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));\n            $tmpOTLdata = false;\n            /* -- OTL -- */\n            if (isset($OTLdata)) {\n                $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);\n                $this->otl->trimOTLdata($tmpOTLdata, false, true);\n                $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);\n            }\n            /* -- END OTL -- */\n            $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);\n        } else {\n            $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);\n        }\n        $this->x = $this->lMargin;\n    }\n\n    /* -- DIRECTW -- */\n\n    function Write($h, $txt, $currentx = 0, $link = '', $directionality = 'ltr', $align = '', $fill = 0)\n    {\n        if (empty($this->directWrite)) {\n            $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);\n        }\n\n        $this->directWrite->Write($h, $txt, $currentx, $link, $directionality, $align, $fill);\n    }\n\n    /* -- END DIRECTW -- */\n\n\n    /* -- HTML-CSS -- */\n\n    function saveInlineProperties()\n    {\n        $saved = [];\n        $saved['family'] = $this->FontFamily;\n        $saved['style'] = $this->FontStyle;\n        $saved['sizePt'] = $this->FontSizePt;\n        $saved['size'] = $this->FontSize;\n        $saved['HREF'] = $this->HREF;\n        $saved['textvar'] = $this->textvar; // mPDF 5.7.1\n        $saved['OTLtags'] = $this->OTLtags; // mPDF 5.7.1\n        $saved['textshadow'] = $this->textshadow;\n        $saved['linewidth'] = $this->LineWidth;\n        $saved['drawcolor'] = $this->DrawColor;\n        $saved['textparam'] = $this->textparam;\n        $saved['lSpacingCSS'] = $this->lSpacingCSS;\n        $saved['wSpacingCSS'] = $this->wSpacingCSS;\n        $saved['I'] = $this->I;\n        $saved['B'] = $this->B;\n        $saved['colorarray'] = $this->colorarray;\n        $saved['bgcolorarray'] = $this->spanbgcolorarray;\n        $saved['border'] = $this->spanborddet;\n        $saved['color'] = $this->TextColor;\n        $saved['bgcolor'] = $this->FillColor;\n        $saved['lang'] = $this->currentLang;\n        $saved['fontLanguageOverride'] = $this->fontLanguageOverride; // mPDF 5.7.1\n        $saved['display_off'] = $this->inlineDisplayOff;\n\n        return $saved;\n    }\n\n    function restoreInlineProperties(&$saved)\n    {\n        $FontFamily = $saved['family'];\n        $this->FontStyle = $saved['style'];\n        $this->FontSizePt = $saved['sizePt'];\n        $this->FontSize = $saved['size'];\n\n        $this->currentLang = $saved['lang'];\n        $this->fontLanguageOverride = $saved['fontLanguageOverride']; // mPDF 5.7.1\n\n        $this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well\n\n        $this->HREF = $saved['HREF'];\n        $this->textvar = $saved['textvar']; // mPDF 5.7.1\n        $this->OTLtags = $saved['OTLtags']; // mPDF 5.7.1\n        $this->textshadow = $saved['textshadow'];\n        $this->LineWidth = $saved['linewidth'];\n        $this->DrawColor = $saved['drawcolor'];\n        $this->textparam = $saved['textparam'];\n        $this->inlineDisplayOff = $saved['display_off'];\n\n        $this->lSpacingCSS = $saved['lSpacingCSS'];\n        if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {\n            $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);\n        } else {\n            $this->fixedlSpacing = false;\n        }\n        $this->wSpacingCSS = $saved['wSpacingCSS'];\n        if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {\n            $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);\n        } else {\n            $this->minwSpacing = 0;\n        }\n\n        $this->SetFont($FontFamily, $saved['style'], $saved['sizePt'], false);\n\n        $this->currentfontstyle = $saved['style'];\n        $this->currentfontsize = $saved['sizePt'];\n        $this->SetStylesArray(['B' => $saved['B'], 'I' => $saved['I']]); // mPDF 5.7.1\n\n        $this->TextColor = $saved['color'];\n        $this->FillColor = $saved['bgcolor'];\n        $this->colorarray = $saved['colorarray'];\n        $cor = $saved['colorarray'];\n        if ($cor) {\n            $this->SetTColor($cor);\n        }\n        $this->spanbgcolorarray = $saved['bgcolorarray'];\n        $cor = $saved['bgcolorarray'];\n        if ($cor) {\n            $this->SetFColor($cor);\n        }\n        $this->spanborddet = $saved['border'];\n    }\n\n    // Used when ColActive for tables - updated to return first block with background fill OR borders\n    function GetFirstBlockFill()\n    {\n        // Returns the first blocklevel that uses a bgcolor fill\n        $startfill = 0;\n        for ($i = 1; $i <= $this->blklvl; $i++) {\n            if ($this->blk[$i]['bgcolor'] || $this->blk[$i]['border_left']['w'] || $this->blk[$i]['border_right']['w'] || $this->blk[$i]['border_top']['w'] || $this->blk[$i]['border_bottom']['w']) {\n                $startfill = $i;\n                break;\n            }\n        }\n        return $startfill;\n    }\n\n    // -------------------------FLOWING BLOCK------------------------------------//\n    // The following functions were originally written by Damon Kohler           //\n    // --------------------------------------------------------------------------//\n\n    function saveFont()\n    {\n        $saved = [];\n        $saved['family'] = $this->FontFamily;\n        $saved['style'] = $this->FontStyle;\n        $saved['sizePt'] = $this->FontSizePt;\n        $saved['size'] = $this->FontSize;\n        $saved['curr'] = &$this->CurrentFont;\n        $saved['lang'] = $this->currentLang; // mPDF 6\n        $saved['color'] = $this->TextColor;\n        $saved['spanbgcolor'] = $this->spanbgcolor;\n        $saved['spanbgcolorarray'] = $this->spanbgcolorarray;\n        $saved['bord'] = $this->spanborder;\n        $saved['border'] = $this->spanborddet;\n        $saved['HREF'] = $this->HREF;\n        $saved['textvar'] = $this->textvar; // mPDF 5.7.1\n        $saved['textshadow'] = $this->textshadow;\n        $saved['linewidth'] = $this->LineWidth;\n        $saved['drawcolor'] = $this->DrawColor;\n        $saved['textparam'] = $this->textparam;\n        $saved['ReqFontStyle'] = $this->ReqFontStyle;\n        $saved['fixedlSpacing'] = $this->fixedlSpacing;\n        $saved['minwSpacing'] = $this->minwSpacing;\n        return $saved;\n    }\n\n    function restoreFont(&$saved, $write = true)\n    {\n        if (!isset($saved) || empty($saved)) {\n            return;\n        }\n\n        $this->FontFamily = $saved['family'];\n        $this->FontStyle = $saved['style'];\n        $this->FontSizePt = $saved['sizePt'];\n        $this->FontSize = $saved['size'];\n        $this->CurrentFont = &$saved['curr'];\n        $this->currentLang = $saved['lang']; // mPDF 6\n        $this->TextColor = $saved['color'];\n        $this->spanbgcolor = $saved['spanbgcolor'];\n        $this->spanbgcolorarray = $saved['spanbgcolorarray'];\n        $this->spanborder = $saved['bord'];\n        $this->spanborddet = $saved['border'];\n        $this->ColorFlag = ($this->FillColor != $this->TextColor); // Restore ColorFlag as well\n        $this->HREF = $saved['HREF'];\n        $this->fixedlSpacing = $saved['fixedlSpacing'];\n        $this->minwSpacing = $saved['minwSpacing'];\n        $this->textvar = $saved['textvar'];  // mPDF 5.7.1\n        $this->textshadow = $saved['textshadow'];\n        $this->LineWidth = $saved['linewidth'];\n        $this->DrawColor = $saved['drawcolor'];\n        $this->textparam = $saved['textparam'];\n        if ($write) {\n            $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], true, true); // force output\n            $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));\n            if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {\n                $this->writer->write($fontout);\n            }\n            $this->pageoutput[$this->page]['Font'] = $fontout;\n        } else {\n            $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], false);\n        }\n        $this->ReqFontStyle = $saved['ReqFontStyle'];\n    }\n\n    function newFlowingBlock($w, $h, $a = '', $is_table = false, $blockstate = 0, $newblock = true, $blockdir = 'ltr', $table_draft = false)\n    {\n        if (!$a) {\n            if ($blockdir == 'rtl') {\n                $a = 'R';\n            } else {\n                $a = 'L';\n            }\n        }\n        $this->flowingBlockAttr['width'] = ($w * Mpdf::SCALE);\n        // line height in user units\n        $this->flowingBlockAttr['is_table'] = $is_table;\n        $this->flowingBlockAttr['table_draft'] = $table_draft;\n        $this->flowingBlockAttr['height'] = $h;\n        $this->flowingBlockAttr['lineCount'] = 0;\n        $this->flowingBlockAttr['align'] = $a;\n        $this->flowingBlockAttr['font'] = [];\n        $this->flowingBlockAttr['content'] = [];\n        $this->flowingBlockAttr['contentB'] = [];\n        $this->flowingBlockAttr['contentWidth'] = 0;\n        $this->flowingBlockAttr['blockstate'] = $blockstate;\n\n        $this->flowingBlockAttr['newblock'] = $newblock;\n        $this->flowingBlockAttr['valign'] = 'M';\n        $this->flowingBlockAttr['blockdir'] = $blockdir;\n        $this->flowingBlockAttr['cOTLdata'] = []; // mPDF 5.7.1\n        $this->flowingBlockAttr['lastBidiText'] = ''; // mPDF 5.7.1\n        if (!empty($this->otl)) {\n            $this->otl->lastBidiStrongType = '';\n        } // *OTL*\n    }\n\n    function finishFlowingBlock($endofblock = false, $next = '')\n    {\n        $currentx = $this->x;\n        // prints out the last chunk\n        $is_table = $this->flowingBlockAttr['is_table'];\n        $table_draft = $this->flowingBlockAttr['table_draft'];\n        $maxWidth = & $this->flowingBlockAttr['width'];\n        $stackHeight = & $this->flowingBlockAttr['height'];\n        $align = & $this->flowingBlockAttr['align'];\n        $content = & $this->flowingBlockAttr['content'];\n        $contentB = & $this->flowingBlockAttr['contentB'];\n        $font = & $this->flowingBlockAttr['font'];\n        $contentWidth = & $this->flowingBlockAttr['contentWidth'];\n        $lineCount = & $this->flowingBlockAttr['lineCount'];\n        $valign = & $this->flowingBlockAttr['valign'];\n        $blockstate = $this->flowingBlockAttr['blockstate'];\n\n        $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1\n        $newblock = $this->flowingBlockAttr['newblock'];\n        $blockdir = $this->flowingBlockAttr['blockdir'];\n\n        // *********** BLOCK BACKGROUND COLOR *****************//\n        if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {\n            $fill = 0;\n        } else {\n            $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n            $fill = 0;\n        }\n\n        $hanger = '';\n        // Always right trim!\n        // Right trim last content and adjust width if needed to justify (later)\n        if (isset($content[count($content) - 1]) && preg_match('/[ ]+$/', $content[count($content) - 1], $m)) {\n            $strip = strlen($m[0]);\n            $content[count($content) - 1] = substr($content[count($content) - 1], 0, (strlen($content[count($content) - 1]) - $strip));\n            /* -- OTL -- */\n            if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true);\n            }\n            /* -- END OTL -- */\n        }\n\n        // the amount of space taken up so far in user units\n        $usedWidth = 0;\n\n        // COLS\n        $oldcolumn = $this->CurrCol;\n\n        if ($this->ColActive && !$is_table) {\n            $this->breakpoints[$this->CurrCol][] = $this->y;\n        } // *COLUMNS*\n        // Print out each chunk\n\n        /* -- TABLES -- */\n        if ($is_table) {\n            $ipaddingL = 0;\n            $ipaddingR = 0;\n            $paddingL = 0;\n            $paddingR = 0;\n        } else {\n            /* -- END TABLES -- */\n            $ipaddingL = $this->blk[$this->blklvl]['padding_left'];\n            $ipaddingR = $this->blk[$this->blklvl]['padding_right'];\n            $paddingL = ($ipaddingL * Mpdf::SCALE);\n            $paddingR = ($ipaddingR * Mpdf::SCALE);\n            $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];\n            $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];\n\n            // Added mPDF 3.0 Float DIV\n            $fpaddingR = 0;\n            $fpaddingL = 0;\n            /* -- CSS-FLOAT -- */\n            if (count($this->floatDivs)) {\n                [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width] = $this->GetFloatDivInfo($this->blklvl);\n                if ($r_exists) {\n                    $fpaddingR = $r_width;\n                }\n                if ($l_exists) {\n                    $fpaddingL = $l_width;\n                }\n            }\n            /* -- END CSS-FLOAT -- */\n\n            $usey = $this->y + 0.002;\n            if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {\n                $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];\n            }\n            /* -- CSS-IMAGE-FLOAT -- */\n            // If float exists at this level\n            if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {\n                $fpaddingR += $this->floatmargins['R']['w'];\n            }\n            if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {\n                $fpaddingL += $this->floatmargins['L']['w'];\n            }\n            /* -- END CSS-IMAGE-FLOAT -- */\n        } // *TABLES*\n\n\n        $lineBox = [];\n\n        $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);\n\n        if ($is_table && count($content) == 0) {\n            $stackHeight = 0;\n        }\n\n        if ($table_draft) {\n            $this->y += $stackHeight;\n            $this->objectbuffer = [];\n            return 0;\n        }\n\n        // While we're at it, check if contains cursive text\n        // Change NBSP to SPACE.\n        // Re-calculate contentWidth\n        $contentWidth = 0;\n\n        foreach ($content as $k => $chunk) {\n            $this->restoreFont($font[$k], false);\n            if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {\n                // Soft Hyphens chr(173)\n                if (!$this->usingCoreFont) {\n                    /* -- OTL -- */\n                    // mPDF 5.7.1\n                    if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                        $this->otl->removeChar($chunk, $cOTLdata[$k], \"\\xc2\\xad\");\n                        $this->otl->replaceSpace($chunk, $cOTLdata[$k]);\n                        $content[$k] = $chunk;\n                    } /* -- END OTL -- */ else {  // *OTL*\n                        $content[$k] = $chunk = str_replace(\"\\xc2\\xad\", '', $chunk);\n                        $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);\n                    } // *OTL*\n                } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {\n                    $content[$k] = $chunk = str_replace(chr(173), '', $chunk);\n                    $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);\n                }\n                $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE;\n            } elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {\n                // LIST MARKERS\t// mPDF 6  Lists\n                if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {\n                    // do nothing\n                } else {\n                    $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;\n                }\n            }\n        }\n\n        if (isset($font[count($font) - 1])) {\n            $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');\n            $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');\n        } else {\n            $lastfontreqstyle = null;\n            $lastfontstyle = null;\n        }\n        if ($blockdir == 'ltr' && strpos($lastfontreqstyle, \"I\") !== false && strpos($lastfontstyle, \"I\") === false) { // Artificial italic\n            $lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;\n        } else {\n            $lastitalic = 0;\n        }\n\n        // Get PAGEBREAK TO TEST for height including the bottom border/padding\n        $check_h = max($this->divheight, $stackHeight);\n\n        // This fixes a proven bug...\n        if ($endofblock && $newblock && $blockstate == 0 && !$content) {\n            $check_h = 0;\n        }\n        // but ? needs to fix potentially more widespread...\n        // if (!$content) {  $check_h = 0; }\n\n        if ($this->blklvl > 0 && !$is_table) {\n            if ($endofblock && $blockstate > 1) {\n                if ($this->blk[$this->blklvl]['page_break_after_avoid']) {\n                    $check_h += $stackHeight;\n                }\n                $check_h += ($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);\n            }\n            if (($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0) || ($endofblock && $blockstate == 3 && $lineCount == 0)) {\n                $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);\n            }\n        }\n\n        // Force PAGE break if column height cannot take check-height\n        if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {\n            $this->SetCol($this->NbCol - 1);\n        }\n\n        // Avoid just border/background-color moved on to next page\n        if ($endofblock && $blockstate > 1 && !$content) {\n            $buff = $this->margBuffer;\n        } else {\n            $buff = 0;\n        }\n\n\n        // PAGEBREAK\n        if (!$is_table && ($this->y + $check_h) > ($this->PageBreakTrigger + $buff) and ! $this->InFooter and $this->AcceptPageBreak()) {\n            $bak_x = $this->x; // Current X position\n            // WORD SPACING\n            $ws = $this->ws; // Word Spacing\n            $charspacing = $this->charspacing; // Character Spacing\n            $this->ResetSpacing();\n\n            $this->AddPage($this->CurOrientation);\n\n            $this->x = $bak_x;\n            // Added to correct for OddEven Margins\n            $currentx += $this->MarginCorrection;\n            $this->x += $this->MarginCorrection;\n\n            // WORD SPACING\n            $this->SetSpacing($charspacing, $ws);\n        }\n\n\n        /* -- COLUMNS -- */\n        // COLS\n        // COLUMN CHANGE\n        if ($this->CurrCol != $oldcolumn) {\n            $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n            $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n            $oldcolumn = $this->CurrCol;\n        }\n\n\n        if ($this->ColActive && !$is_table) {\n            $this->breakpoints[$this->CurrCol][] = $this->y;\n        }\n        /* -- END COLUMNS -- */\n\n        // TOP MARGIN\n        if ($newblock && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && $lineCount == 0 && !$is_table) {\n            $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);\n            if ($this->ColActive) {\n                $this->breakpoints[$this->CurrCol][] = $this->y;\n            } // *COLUMNS*\n        }\n\n        if ($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0 && !$is_table) {\n            $this->blk[$this->blklvl]['y0'] = $this->y;\n            $this->blk[$this->blklvl]['startpage'] = $this->page;\n            if ($this->blk[$this->blklvl]['float']) {\n                $this->blk[$this->blklvl]['float_start_y'] = $this->y;\n            }\n            if ($this->ColActive) {\n                $this->breakpoints[$this->CurrCol][] = $this->y;\n            } // *COLUMNS*\n        }\n\n        // Paragraph INDENT\n        $WidthCorrection = 0;\n        if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {\n            $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4\n            $WidthCorrection = ($ti * Mpdf::SCALE);\n        }\n\n\n        // PADDING and BORDER spacing/fill\n        if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 0) && (!$is_table)) {\n            // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom\n            $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);\n            if ($this->ColActive) {\n                $this->breakpoints[$this->CurrCol][] = $this->y;\n            } // *COLUMNS*\n            $this->x = $currentx;\n        }\n\n\n        // Added mPDF 3.0 Float DIV\n        $fpaddingR = 0;\n        $fpaddingL = 0;\n        /* -- CSS-FLOAT -- */\n        if (count($this->floatDivs)) {\n            [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width] = $this->GetFloatDivInfo($this->blklvl);\n            if ($r_exists) {\n                $fpaddingR = $r_width;\n            }\n            if ($l_exists) {\n                $fpaddingL = $l_width;\n            }\n        }\n        /* -- END CSS-FLOAT -- */\n\n        $usey = $this->y + 0.002;\n        if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {\n            $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];\n        }\n        /* -- CSS-IMAGE-FLOAT -- */\n        // If float exists at this level\n        if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {\n            $fpaddingR += $this->floatmargins['R']['w'];\n        }\n        if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {\n            $fpaddingL += $this->floatmargins['L']['w'];\n        }\n        /* -- END CSS-IMAGE-FLOAT -- */\n\n\n        if ($content) {\n            // In FinishFlowing Block no lines are justified as it is always last line\n            // but if CJKorphan has allowed content width to go over max width, use J charspacing to compress line\n            // JUSTIFICATION J - NOT!\n            $nb_carac = 0;\n            $nb_spaces = 0;\n            $jcharspacing = 0;\n            $jkashida = 0;\n            $jws = 0;\n            $inclCursive = false;\n            $dottab = false;\n            foreach ($content as $k => $chunk) {\n                if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {\n                    $nb_carac += mb_strlen($chunk, $this->mb_enc);\n                    $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);\n                    // mPDF 6\n                    // Use GPOS OTL\n                    $this->restoreFont($font[$k], false);\n                    if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                        if (isset($cOTLdata[$k]['group']) && $cOTLdata[$k]['group']) {\n                            $nb_marks = substr_count($cOTLdata[$k]['group'], 'M');\n                            $nb_carac -= $nb_marks;\n                        }\n                        if (preg_match(\"/([\" . $this->pregCURSchars . \"])/u\", $chunk)) {\n                            $inclCursive = true;\n                        }\n                    }\n                } else {\n                    $nb_carac ++;  // mPDF 6 allow spacing for inline object\n                    if ($this->objectbuffer[$k]['type'] == 'dottab') {\n                        $dottab = $this->objectbuffer[$k]['outdent'];\n                    }\n                }\n            }\n\n            // DIRECTIONALITY RTL\n            $chunkorder = range(0, count($content) - 1); // mPDF 6\n            /* -- OTL -- */\n            // mPDF 6\n            if ($blockdir == 'rtl' || $this->biDirectional) {\n                $this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);\n                // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to\n                // $this->objectbuffer and $font ($chunkorder contains the mapping)\n            }\n            /* -- END OTL -- */\n\n            // Remove any XAdvance from OTL data at end of line\n            // And correct for XPlacement on last character\n            // BIDI is applied\n            foreach ($chunkorder as $aord => $k) {\n                if (count($cOTLdata)) {\n                    $this->restoreFont($font[$k], false);\n                    // ...FinishFlowingBlock...\n                    if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line\n                        $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character\n                        if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {\n                            if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {\n                                $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                            } else {\n                                $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                            }\n                            $w *= ($this->FontSize / 1000);\n                            $contentWidth -= $w * Mpdf::SCALE;\n                            $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;\n                            $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;\n                        }\n\n                        // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it\n                        if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {\n                            $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                            $w *= ($this->FontSize / 1000);\n                            $contentWidth -= $w * Mpdf::SCALE;\n                            $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];\n                            $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];\n                        }\n                    }\n                }\n            }\n\n            // if it's justified, we need to find the char/word spacing (or if orphans have allowed length of line to go over the maxwidth)\n            // If \"orphans\" in fact is just a final space - ignore this\n            $lastchar = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);\n            if (preg_match(\"/[\" . $this->CJKoverflow . \"]/u\", $lastchar)) {\n                $CJKoverflow = true;\n            } else {\n                $CJKoverflow = false;\n            }\n            if ((((($contentWidth + $lastitalic) > $maxWidth) && ($content[(count($chunkorder) - 1)] != ' ') ) ||\n                    (!$endofblock && $align == 'J' && ($next == 'image' || $next == 'select' || $next == 'input' || $next == 'textarea' || ($next == 'br' && $this->justifyB4br)))) && !($CJKoverflow && $this->allowCJKoverflow)) {\n                // WORD SPACING\n                [$jcharspacing, $jws, $jkashida] = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata);\n            } /* -- CJK-FONTS -- */ elseif ($this->checkCJK && $align == 'J' && $CJKoverflow && $this->allowCJKoverflow && $this->CJKforceend) {\n                // force-end overhang\n                $hanger = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);\n                if (preg_match(\"/[\" . $this->CJKoverflow . \"]/u\", $hanger)) {\n                    $content[(count($chunkorder) - 1)] = mb_substr($content[(count($chunkorder) - 1)], 0, mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, $this->mb_enc);\n                    $this->restoreFont($font[$chunkorder[count($chunkorder) - 1]], false);\n                    $contentWidth -= $this->GetStringWidth($hanger) * Mpdf::SCALE;\n                    $nb_carac -= 1;\n                    [$jcharspacing, $jws, $jkashida] = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata);\n                }\n            } /* -- END CJK-FONTS -- */\n\n            // Check if will fit at word/char spacing of previous line - if so continue it\n            // but only allow a maximum of $this->jSmaxWordLast and $this->jSmaxCharLast\n            elseif ($contentWidth < ($maxWidth - $lastitalic - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE))) && !$this->fixedlSpacing) {\n                if ($this->ws > $this->jSmaxWordLast) {\n                    $jws = $this->jSmaxWordLast;\n                }\n                if ($this->charspacing > $this->jSmaxCharLast) {\n                    $jcharspacing = $this->jSmaxCharLast;\n                }\n                $check = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) - ( $jcharspacing * $nb_carac) - ( $jws * $nb_spaces);\n                if ($check <= 0) {\n                    $jcharspacing = 0;\n                    $jws = 0;\n                }\n            }\n\n            $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );\n\n\n            $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1\n            $empty -= ($jws * $nb_spaces);\n            $empty -= ($jkashida);\n\n            $empty /= Mpdf::SCALE;\n\n            if (!$is_table) {\n                $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin'] - $empty));\n                $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $empty));\n            }\n\n            $arraysize = count($chunkorder);\n\n            $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );\n\n            if (!$is_table) {\n                $this->DivLn($stackHeight, $this->blklvl, false);\n            } // false -> don't advance y\n\n            $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;\n            if ($dottab !== false && $blockdir == 'rtl') {\n                $this->x -= $dottab;\n            } elseif ($align == 'R') {\n                $this->x += $empty;\n            } elseif ($align == 'J' && $blockdir == 'rtl') {\n                $this->x += $empty;\n            } elseif ($align == 'C') {\n                $this->x += ($empty / 2);\n            }\n\n            // Paragraph INDENT\n            $WidthCorrection = 0;\n            if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {\n                $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4\n                if ($blockdir != 'rtl') {\n                    $this->x += $ti;\n                } // mPDF 6\n            }\n\n            foreach ($chunkorder as $aord => $k) { // mPDF 5.7\n                $chunk = $content[$aord];\n                if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {\n                    $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];\n                    $this->objectbuffer[$k]['OUTER-X'] += $xadj;\n                    $this->objectbuffer[$k]['BORDER-X'] += $xadj;\n                    $this->objectbuffer[$k]['INNER-X'] += $xadj;\n\n                    if ($this->objectbuffer[$k]['type'] == 'listmarker') {\n                        $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin\n                    }\n                    $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];\n                    if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB\n                        $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin\n                    }\n                    if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB\n                        $yadj += $lineBox[$k]['top'];\n                    }\n                    $this->objectbuffer[$k]['OUTER-Y'] += $yadj;\n                    $this->objectbuffer[$k]['BORDER-Y'] += $yadj;\n                    $this->objectbuffer[$k]['INNER-Y'] += $yadj;\n                }\n\n                $this->restoreFont($font[$k]);  // mPDF 5.7\n\n                if ($is_table && substr($align, 0, 1) == 'D' && $aord == 0) {\n                    $dp = $this->decimal_align[substr($align, 0, 2)];\n                    $s = preg_split('/' . preg_quote($dp, '/') . '/', $content[0], 2);  // ? needs to be /u if not core\n                    $s0 = $this->GetStringWidth($s[0], false);\n                    $this->x += ($this->decimal_offset - $s0);\n                }\n\n                $this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);\n                $this->fixedlSpacing = false;\n                $this->minwSpacing = 0;\n\n                $save_vis = $this->visibility;\n                if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {\n                    $this->SetVisibility($this->textparam['visibility']);\n                }\n\n                // *********** SPAN BACKGROUND COLOR ***************** //\n                if (isset($this->spanbgcolor) && $this->spanbgcolor) {\n                    $cor = $this->spanbgcolorarray;\n                    $this->SetFColor($cor);\n                    $save_fill = $fill;\n                    $spanfill = 1;\n                    $fill = 1;\n                }\n                if (!empty($this->spanborddet)) {\n                    if (strpos($contentB[$k], 'L') !== false && isset($this->spanborddet['L'])) {\n                        $this->x += $this->spanborddet['L']['w'];\n                    }\n                    if (strpos($contentB[$k], 'L') === false) {\n                        $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;\n                    }\n                    if (strpos($contentB[$k], 'R') === false) {\n                        $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;\n                    }\n                }\n                // WORD SPACING\n                // mPDF 5.7.1\n                $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar);\n                $nch = mb_strlen($chunk, $this->mb_enc);\n                // Use GPOS OTL\n                if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                    if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {\n                        $nch -= substr_count($cOTLdata[$aord]['group'], 'M');\n                    }\n                }\n                $stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );\n\n                $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );\n\n                if (isset($this->objectbuffer[$k])) {\n                    if ($this->objectbuffer[$k]['type'] == 'dottab') {\n                        $this->objectbuffer[$k]['OUTER-WIDTH'] +=$empty;\n                        $this->objectbuffer[$k]['OUTER-WIDTH'] +=$this->objectbuffer[$k]['outdent'];\n                    }\n                    // LIST MARKERS\t// mPDF 6  Lists\n                    if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {\n                        // do nothing\n                    } else {\n                        $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];\n                    }\n                }\n\n                if ($stringWidth == 0) {\n                    $stringWidth = 0.000001;\n                }\n                if ($aord == $arraysize - 1) { // mPDF 5.7\n                    // mPDF 5.7.1\n                    if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {\n                        // force-end overhang\n                        $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));  // mPDF 5.7.1\n                        $this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1\n                    } else {\n                        $this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1\n                    }\n                } else {\n                    $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // first or middle part\t// mPDF 5.7.1\n                }\n\n\n                if (!empty($this->spanborddet)) {\n                    if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {\n                        $this->x += $this->spanborddet['R']['w'];\n                    }\n                }\n                // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //\n                if (isset($spanfill) && $spanfill) {\n                    $fill = $save_fill;\n                    $spanfill = 0;\n                    if ($fill) {\n                        $this->SetFColor($bcor);\n                    }\n                }\n                if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {\n                    $this->SetVisibility($save_vis);\n                }\n            }\n\n            $this->printobjectbuffer($is_table, $blockdir);\n            $this->objectbuffer = [];\n            $this->ResetSpacing();\n        } // END IF CONTENT\n\n        /* -- CSS-IMAGE-FLOAT -- */\n        // Update values if set to skipline\n        if ($this->floatmargins) {\n            $this->_advanceFloatMargins();\n        }\n\n\n        if ($endofblock && $blockstate > 1) {\n            // If float exists at this level\n            if (isset($this->floatmargins['R']['y1'])) {\n                $fry1 = $this->floatmargins['R']['y1'];\n            } else {\n                $fry1 = 0;\n            }\n            if (isset($this->floatmargins['L']['y1'])) {\n                $fly1 = $this->floatmargins['L']['y1'];\n            } else {\n                $fly1 = 0;\n            }\n            if ($this->y < $fry1 || $this->y < $fly1) {\n                $drop = max($fry1, $fly1) - $this->y;\n                $this->DivLn($drop);\n                $this->x = $currentx;\n            }\n        }\n        /* -- END CSS-IMAGE-FLOAT -- */\n\n\n        // PADDING and BORDER spacing/fill\n        if ($endofblock && ($blockstate > 1) && ($this->blk[$this->blklvl]['padding_bottom'] || $this->blk[$this->blklvl]['border_bottom'] || $this->blk[$this->blklvl]['css_set_height']) && (!$is_table)) {\n            // If CSS height set, extend bottom - if on same page as block started, and CSS HEIGHT > actual height,\n            // and does not force pagebreak\n            $extra = 0;\n            if (isset($this->blk[$this->blklvl]['css_set_height']) && $this->blk[$this->blklvl]['css_set_height'] && $this->blk[$this->blklvl]['startpage'] == $this->page) {\n                // predicted height\n                $h1 = ($this->y - $this->blk[$this->blklvl]['y0']) + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];\n                if ($h1 < ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top'])) {\n                    $extra = ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top']) - $h1;\n                }\n                if ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra > $this->PageBreakTrigger) {\n                    $extra = $this->PageBreakTrigger - ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);\n                }\n            }\n\n            // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom\n            $this->DivLn($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra, -3, true, false, 2);\n            $this->x = $currentx;\n\n            if ($this->ColActive) {\n                $this->breakpoints[$this->CurrCol][] = $this->y;\n            } // *COLUMNS*\n        }\n\n        // SET Bottom y1 of block (used for painting borders)\n        if (($endofblock) && ($blockstate > 1) && (!$is_table)) {\n            $this->blk[$this->blklvl]['y1'] = $this->y;\n        }\n\n        // BOTTOM MARGIN\n        if (($endofblock) && ($blockstate > 1) && ($this->blk[$this->blklvl]['margin_bottom']) && (!$is_table)) {\n            if ($this->y + $this->blk[$this->blklvl]['margin_bottom'] < $this->PageBreakTrigger and ! $this->InFooter) {\n                $this->DivLn($this->blk[$this->blklvl]['margin_bottom'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);\n                if ($this->ColActive) {\n                    $this->breakpoints[$this->CurrCol][] = $this->y;\n                } // *COLUMNS*\n            }\n        }\n\n        // Reset lineheight\n        $stackHeight = $this->divheight;\n    }\n\n    function printobjectbuffer($is_table = false, $blockdir = false)\n    {\n        if (!$blockdir) {\n            $blockdir = $this->directionality;\n        }\n\n        if ($is_table && $this->shrin_k > 1) {\n            $k = $this->shrin_k;\n        } else {\n            $k = 1;\n        }\n\n        $save_y = $this->y;\n        $save_x = $this->x;\n\n        $save_currentfontfamily = $this->FontFamily;\n        $save_currentfontsize = $this->FontSizePt;\n        $save_currentfontstyle = $this->FontStyle;\n\n        if ($blockdir == 'rtl') {\n            $rtlalign = 'R';\n        } else {\n            $rtlalign = 'L';\n        }\n\n        foreach ($this->objectbuffer as $ib => $objattr) {\n\n            if ($objattr['type'] == 'bookmark' || $objattr['type'] == 'indexentry' || $objattr['type'] == 'toc') {\n                $x = $objattr['OUTER-X'];\n                $y = $objattr['OUTER-Y'];\n                $this->y = $y - $this->FontSize / 2;\n                $this->x = $x;\n                if ($objattr['type'] == 'bookmark') {\n                    $this->Bookmark($objattr['CONTENT'], $objattr['bklevel'], $y - $this->FontSize);\n                } // *BOOKMARKS*\n                if ($objattr['type'] == 'indexentry') {\n                    $this->IndexEntry($objattr['CONTENT']);\n                } // *INDEX*\n                if ($objattr['type'] == 'toc') {\n                    $this->TOC_Entry($objattr['CONTENT'], $objattr['toclevel'], (isset($objattr['toc_id']) ? $objattr['toc_id'] : ''));\n                } // *TOC*\n            } /* -- ANNOTATIONS -- */ elseif ($objattr['type'] == 'annot') {\n                if ($objattr['POS-X']) {\n                    $x = $objattr['POS-X'];\n                } elseif ($this->annotMargin <> 0) {\n                    $x = -$objattr['OUTER-X'];\n                } else {\n                    $x = $objattr['OUTER-X'];\n                }\n                if ($objattr['POS-Y']) {\n                    $y = $objattr['POS-Y'];\n                } else {\n                    $y = $objattr['OUTER-Y'] - $this->FontSize / 2;\n                }\n                // Create a dummy entry in the _out/columnBuffer with position sensitive data,\n                // linking $y-1 in the Columnbuffer with entry in $this->columnAnnots\n                // and when columns are split in length will not break annotation from current line\n                $this->y = $y - 1;\n                $this->x = $x - 1;\n                $this->Line($x - 1, $y - 1, $x - 1, $y - 1);\n                $this->Annotation($objattr['CONTENT'], $x, $y, $objattr['ICON'], $objattr['AUTHOR'], $objattr['SUBJECT'], $objattr['OPACITY'], $objattr['COLOR'], (isset($objattr['POPUP']) ? $objattr['POPUP'] : ''), (isset($objattr['FILE']) ? $objattr['FILE'] : ''));\n            } /* -- END ANNOTATIONS -- */ else {\n                $y = $objattr['OUTER-Y'];\n                $x = $objattr['OUTER-X'];\n                $w = $objattr['OUTER-WIDTH'];\n                $h = $objattr['OUTER-HEIGHT'];\n                if (isset($objattr['text'])) {\n                    $texto = $objattr['text'];\n                }\n                $this->y = $y;\n                $this->x = $x;\n                if (isset($objattr['fontfamily'])) {\n                    $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);\n                }\n            }\n\n            // HR\n            if ($objattr['type'] == 'hr') {\n                $this->SetDColor($objattr['color']);\n                switch ($objattr['align']) {\n                    case 'C':\n                        $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];\n                        $empty /= 2;\n                        $x += $empty;\n                        break;\n                    case 'R':\n                        $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];\n                        $x += $empty;\n                        break;\n                }\n                $oldlinewidth = $this->LineWidth;\n                $this->SetLineWidth($objattr['linewidth'] / $k);\n                $this->y += ($objattr['linewidth'] / 2) + $objattr['margin_top'] / $k;\n                $this->Line($x, $this->y, $x + $objattr['INNER-WIDTH'], $this->y);\n                $this->SetLineWidth($oldlinewidth);\n                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            }\n            // IMAGE\n            if ($objattr['type'] == 'image') {\n                // mPDF 5.7.3 TRANSFORMS\n                if (isset($objattr['transform'])) {\n                    $this->writer->write(\"\\n\" . '% BTR'); // Begin Transform\n                }\n                if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {\n                    $this->BeginLayer($objattr['z-index']);\n                }\n                if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {\n                    $this->SetVisibility($objattr['visibility']);\n                }\n                if (isset($objattr['opacity'])) {\n                    $this->SetAlpha($objattr['opacity']);\n                }\n\n                $obiw = $objattr['INNER-WIDTH'];\n                $obih = $objattr['INNER-HEIGHT'];\n\n                $sx = $objattr['orig_w'] ? ($objattr['INNER-WIDTH'] * Mpdf::SCALE / $objattr['orig_w']) : INF;\n                $sy = $objattr['orig_h'] ? ($objattr['INNER-HEIGHT'] * Mpdf::SCALE / $objattr['orig_h']) : INF;\n\n                $rotate = 0;\n                if (isset($objattr['ROTATE'])) {\n                    $rotate = $objattr['ROTATE'];\n                }\n\n                if ($rotate == 90) {\n                    // Clockwise\n                    $obiw = $objattr['INNER-HEIGHT'];\n                    $obih = $objattr['INNER-WIDTH'];\n                    $tr = $this->transformTranslate(0, -$objattr['INNER-WIDTH'], true);\n                    $tr .= ' ' . $this->transformRotate(90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);\n                    $sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];\n                    $sy = $obih * Mpdf::SCALE / $objattr['orig_w'];\n                } elseif ($rotate == -90 || $rotate == 270) {\n                    // AntiClockwise\n                    $obiw = $objattr['INNER-HEIGHT'];\n                    $obih = $objattr['INNER-WIDTH'];\n                    $tr = $this->transformTranslate($objattr['INNER-WIDTH'], ($objattr['INNER-HEIGHT'] - $objattr['INNER-WIDTH']), true);\n                    $tr .= ' ' . $this->transformRotate(-90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);\n                    $sx = $obiw * Mpdf::SCALE / $objattr['orig_h'];\n                    $sy = $obih * Mpdf::SCALE / $objattr['orig_w'];\n                } elseif ($rotate == 180) {\n                    // Mirror\n                    $tr = $this->transformTranslate($objattr['INNER-WIDTH'], -$objattr['INNER-HEIGHT'], true);\n                    $tr .= ' ' . $this->transformRotate(180, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-HEIGHT']), true);\n                } else {\n                    $tr = '';\n                }\n                $tr = trim($tr);\n                if ($tr) {\n                    $tr .= ' ';\n                }\n                $gradmask = '';\n\n                // mPDF 5.7.3 TRANSFORMS\n                $tr2 = '';\n                if (isset($objattr['transform'])) {\n                    $maxsize_x = $w;\n                    $maxsize_y = $h;\n                    $cx = $x + $w / 2;\n                    $cy = $y + $h / 2;\n                    preg_match_all('/(translatex|translatey|translate|scalex|scaley|scale|rotate|skewX|skewY|skew)\\((.*?)\\)/is', $objattr['transform'], $m);\n                    if (count($m[0])) {\n                        for ($i = 0; $i < count($m[0]); $i++) {\n                            $c = strtolower($m[1][$i]);\n                            $v = trim($m[2][$i]);\n                            $vv = preg_split('/[ ,]+/', $v);\n                            if ($c == 'translate' && count($vv)) {\n                                $translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);\n                                if (count($vv) == 2) {\n                                    $translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false);\n                                } else {\n                                    $translate_y = 0;\n                                }\n                                $tr2 .= $this->transformTranslate($translate_x, $translate_y, true) . ' ';\n                            } elseif ($c == 'translatex' && count($vv)) {\n                                $translate_x = $this->sizeConverter->convert($vv[0], $maxsize_x, false, false);\n                                $tr2 .= $this->transformTranslate($translate_x, 0, true) . ' ';\n                            } elseif ($c == 'translatey' && count($vv)) {\n                                $translate_y = $this->sizeConverter->convert($vv[1], $maxsize_y, false, false);\n                                $tr2 .= $this->transformTranslate(0, $translate_y, true) . ' ';\n                            } elseif ($c == 'scale' && count($vv)) {\n                                $scale_x = $vv[0] * 100;\n                                if (count($vv) == 2) {\n                                    $scale_y = $vv[1] * 100;\n                                } else {\n                                    $scale_y = $scale_x;\n                                }\n                                $tr2 .= $this->transformScale($scale_x, $scale_y, $cx, $cy, true) . ' ';\n                            } elseif ($c == 'scalex' && count($vv)) {\n                                $scale_x = $vv[0] * 100;\n                                $tr2 .= $this->transformScale($scale_x, 0, $cx, $cy, true) . ' ';\n                            } elseif ($c == 'scaley' && count($vv)) {\n                                $scale_y = $vv[1] * 100;\n                                $tr2 .= $this->transformScale(0, $scale_y, $cx, $cy, true) . ' ';\n                            } elseif ($c == 'skew' && count($vv)) {\n                                $angle_x = $this->ConvertAngle($vv[0], false);\n                                if (count($vv) == 2) {\n                                    $angle_y = $this->ConvertAngle($vv[1], false);\n                                } else {\n                                    $angle_y = 0;\n                                }\n                                $tr2 .= $this->transformSkew($angle_x, $angle_y, $cx, $cy, true) . ' ';\n                            } elseif ($c == 'skewx' && count($vv)) {\n                                $angle = $this->ConvertAngle($vv[0], false);\n                                $tr2 .= $this->transformSkew($angle, 0, $cx, $cy, true) . ' ';\n                            } elseif ($c == 'skewy' && count($vv)) {\n                                $angle = $this->ConvertAngle($vv[0], false);\n                                $tr2 .= $this->transformSkew(0, $angle, $cx, $cy, true) . ' ';\n                            } elseif ($c == 'rotate' && count($vv)) {\n                                $angle = $this->ConvertAngle($vv[0]);\n                                $tr2 .= $this->transformRotate($angle, $cx, $cy, true) . ' ';\n                            }\n                        }\n                    }\n                }\n\n                // LIST MARKERS (Images)\t// mPDF 6  Lists\n                if (isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {\n                    $mw = $objattr['OUTER-WIDTH'];\n                    // NB If change marker-offset, also need to alter in function _getListMarkerWidth\n                    $adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);\n                    if ($objattr['dir'] == 'rtl') {\n                        $objattr['INNER-X'] += $adjx;\n                    } else {\n                        $objattr['INNER-X'] -= $adjx;\n                        $objattr['INNER-X'] -= $mw;\n                    }\n                }\n                // mPDF 5.7.3 TRANSFORMS / BACKGROUND COLOR\n                // Transform also affects image background\n                if ($tr2) {\n                    $this->writer->write('q ' . $tr2 . ' ');\n                }\n                if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {\n                    $bgcol = $objattr['bgcolor'];\n                    $this->SetFColor($bgcol);\n                    $this->Rect($x, $y, $w, $h, 'F');\n                    $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n                }\n                if ($tr2) {\n                    $this->writer->write('Q');\n                }\n\n                /* -- BACKGROUNDS -- */\n                if (isset($objattr['GRADIENT-MASK'])) {\n                    $g = $this->gradient->parseMozGradient($objattr['GRADIENT-MASK']);\n                    if ($g) {\n                        $dummy = $this->gradient->Gradient($objattr['INNER-X'], $objattr['INNER-Y'], $obiw, $obih, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true, true);\n                        $gradmask = '/TGS' . count($this->gradients) . ' gs ';\n                    }\n                }\n                /* -- END BACKGROUNDS -- */\n                /* -- IMAGES-WMF -- */\n                if (isset($objattr['itype']) && $objattr['itype'] == 'wmf') {\n                    $outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * Mpdf::SCALE - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * Mpdf::SCALE) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS\n                } else { \t\t\t\t/* -- END IMAGES-WMF -- */\n                    if (isset($objattr['itype']) && $objattr['itype'] == 'svg') {\n                        $outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * Mpdf::SCALE - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * Mpdf::SCALE) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS\n                    } else {\n                        $outstring = sprintf(\"q \" . $tr . $tr2 . \"%.3F 0 0 %.3F %.3F %.3F cm \" . $gradmask . \"/I%d Do Q\", $obiw * Mpdf::SCALE, $obih * Mpdf::SCALE, $objattr['INNER-X'] * Mpdf::SCALE, ($this->h - ($objattr['INNER-Y'] + $obih )) * Mpdf::SCALE, $objattr['ID']); // mPDF 5.7.3 TRANSFORMS\n                    }\n                }\n                $this->writer->write($outstring);\n                // LINK\n                if (isset($objattr['link'])) {\n                    $this->Link($objattr['INNER-X'], $objattr['INNER-Y'], $objattr['INNER-WIDTH'], $objattr['INNER-HEIGHT'], $objattr['link']);\n                }\n                if (isset($objattr['opacity'])) {\n                    $this->SetAlpha(1);\n                }\n\n                // mPDF 5.7.3 TRANSFORMS\n                // Transform also affects image borders\n                if ($tr2) {\n                    $this->writer->write('q ' . $tr2 . ' ');\n                }\n                if ((isset($objattr['border_top']) && $objattr['border_top'] > 0) || (isset($objattr['border_left']) && $objattr['border_left'] > 0) || (isset($objattr['border_right']) && $objattr['border_right'] > 0) || (isset($objattr['border_bottom']) && $objattr['border_bottom'] > 0)) {\n                    $this->PaintImgBorder($objattr, $is_table);\n                }\n                if ($tr2) {\n                    $this->writer->write('Q');\n                }\n\n                if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {\n                    $this->SetVisibility('visible');\n                }\n                if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {\n                    $this->EndLayer();\n                }\n                // mPDF 5.7.3 TRANSFORMS\n                if (isset($objattr['transform'])) {\n                    $this->writer->write(\"\\n\" . '% ETR'); // End Transform\n                }\n            }\n\n            if ($objattr['type'] === 'barcode') {\n\n                $bgcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);\n\n                if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {\n                    $bgcol = $objattr['bgcolor'];\n                }\n\n                $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);\n\n                if (isset($objattr['color']) && $objattr['color']) {\n                    $col = $objattr['color'];\n                }\n\n                $this->SetFColor($bgcol);\n                $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');\n                $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n\n                if (isset($objattr['BORDER-WIDTH'])) {\n                    $this->PaintImgBorder($objattr, $is_table);\n                }\n\n                $barcodeTypes = ['EAN13', 'ISBN', 'ISSN', 'UPCA', 'UPCE', 'EAN8'];\n                if (in_array($objattr['btype'], $barcodeTypes, true)) {\n\n                    $this->WriteBarcode(\n                        $objattr['code'],\n                        $objattr['showtext'],\n                        $objattr['INNER-X'],\n                        $objattr['INNER-Y'],\n                        $objattr['bsize'],\n                        0,\n                        0,\n                        0,\n                        0,\n                        0,\n                        $objattr['bheight'],\n                        $bgcol,\n                        $col,\n                        $objattr['btype'],\n                        $objattr['bsupp'],\n                        (isset($objattr['bsupp_code']) ? $objattr['bsupp_code'] : ''),\n                        $k\n                    );\n\n                } elseif ($objattr['btype'] === 'QR') {\n\n                    if (!class_exists('Mpdf\\QrCode\\QrCode')) {\n                        throw new \\Mpdf\\MpdfException('Class Mpdf\\QrCode\\QrCode does not exists. Install the package from Packagist with \"composer require mpdf/qrcode\"');\n                    }\n\n                    $barcodeContent = str_replace('\\r\\n', \"\\r\\n\", $objattr['code']);\n                    $barcodeContent = str_replace('\\n', \"\\n\", $barcodeContent);\n\n                    $qrcode = new QrCode\\QrCode($barcodeContent, $objattr['errorlevel']);\n                    if ($objattr['disableborder']) {\n                        $qrcode->disableBorder();\n                    }\n\n                    $bgColor = [255, 255, 255];\n                    if ($objattr['bgcolor']) {\n                        $bgColor = array_map(\n                            function ($col) {\n                                return intval(255 * floatval($col));\n                            },\n                            explode(\" \", $this->SetColor($objattr['bgcolor'], 'CodeOnly'))\n                        );\n                    }\n                    $color = [0, 0, 0];\n                    if ($objattr['color']) {\n                        $color = array_map(\n                            function ($col) {\n                                return intval(255 * floatval($col));\n                            },\n                            explode(\" \", $this->SetColor($objattr['color'], 'CodeOnly'))\n                        );\n                    }\n\n                    $out = new QrCode\\Output\\Mpdf();\n                    $out->output(\n                        $qrcode,\n                        $this,\n                        $objattr['INNER-X'],\n                        $objattr['INNER-Y'],\n                        $objattr['bsize'] * 25,\n                        $bgColor,\n                        $color\n                    );\n\n                    unset($qrcode);\n\n                } else {\n\n                    $this->WriteBarcode2(\n                        $objattr['code'],\n                        $objattr['INNER-X'],\n                        $objattr['INNER-Y'],\n                        $objattr['bsize'],\n                        $objattr['bheight'],\n                        $bgcol,\n                        $col,\n                        $objattr['btype'],\n                        $objattr['pr_ratio'],\n                        $k\n                    );\n\n                }\n            }\n\n            // TEXT CIRCLE\n            if ($objattr['type'] == 'textcircle') {\n                $bgcol = '';\n                if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {\n                    $bgcol = $objattr['bgcolor'];\n                }\n                $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);\n                if (isset($objattr['color']) && $objattr['color']) {\n                    $col = $objattr['color'];\n                }\n                $this->SetTColor($col);\n                $this->SetFColor($bgcol);\n                if ($bgcol) {\n                    $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');\n                }\n                $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n                if (isset($objattr['BORDER-WIDTH'])) {\n                    $this->PaintImgBorder($objattr, $is_table);\n                }\n                if (empty($this->directWrite)) {\n                    $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);\n                }\n                if (isset($objattr['top-text'])) {\n                    $this->directWrite->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['top-text'], 'top', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : ''));\n                }\n                if (isset($objattr['bottom-text'])) {\n                    $this->directWrite->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['bottom-text'], 'bottom', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : ''));\n                }\n            }\n\n            $this->ResetSpacing();\n\n            // LIST MARKERS (Text or bullets)\t// mPDF 6  Lists\n            if ($objattr['type'] == 'listmarker') {\n                if (isset($objattr['fontfamily'])) {\n                    $this->SetFont($objattr['fontfamily'], $objattr['fontstyle'], $objattr['fontsizept']);\n                }\n                $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);\n                if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {\n                    $col = $objattr['colorarray'];\n                }\n\n                if (isset($objattr['bullet']) && $objattr['bullet']) { // Used for position \"outside\" only\n                    $type = $objattr['bullet'];\n                    $size = $objattr['size'];\n\n                    if ($objattr['listmarkerposition'] == 'inside') {\n                        $adjx = $size / 2;\n                        if ($objattr['dir'] == 'rtl') {\n                            $adjx += $objattr['offset'];\n                        }\n                        $this->x += $adjx;\n                    } else {\n                        $adjx = $objattr['offset'];\n                        $adjx += $size / 2;\n                        if ($objattr['dir'] == 'rtl') {\n                            $this->x += $adjx;\n                        } else {\n                            $this->x -= $adjx;\n                        }\n                    }\n\n                    $yadj = $objattr['lineBox']['glyphYorigin'];\n                    if (isset($this->CurrentFont['desc']['XHeight']) && $this->CurrentFont['desc']['XHeight']) {\n                        $xh = $this->CurrentFont['desc']['XHeight'];\n                    } else {\n                        $xh = 500;\n                    }\n                    $yadj -= ($this->FontSize * $xh / 1000) * 0.625; // Vertical height of bullet (centre) from baseline= XHeight * 0.625\n                    $this->y += $yadj;\n\n                    $this->_printListBullet($this->x, $this->y, $size, $type, $col);\n                } else {\n                    $this->SetTColor($col);\n                    $w = $this->GetStringWidth($texto);\n                    // NB If change marker-offset, also need to alter in function _getListMarkerWidth\n                    $adjx = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);\n                    if ($objattr['dir'] == 'rtl') {\n                        $align = 'L';\n                        $this->x += $adjx;\n                    } else {\n                        // Use these lines to set as marker-offset, right-aligned - default\n                        $align = 'R';\n                        $this->x -= $adjx;\n                        $this->x -= $w;\n                    }\n                    $this->Cell($w, $this->FontSize, $texto, 0, 0, $align, 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']);\n                    $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                }\n            }\n\n            // DOT-TAB\n            if ($objattr['type'] == 'dottab') {\n                if (isset($objattr['fontfamily'])) {\n                    $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);\n                }\n                $sp = $this->GetStringWidth(' ');\n                $nb = floor(($w - 2 * $sp) / $this->GetStringWidth('.'));\n                if ($nb > 0) {\n                    $dots = ' ' . str_repeat('.', $nb) . ' ';\n                } else {\n                    $dots = ' ';\n                }\n                $col = $this->colorConverter->convert(0, $this->PDFAXwarnings);\n                if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {\n                    $col = $objattr['colorarray'];\n                }\n                $this->SetTColor($col);\n                $save_dh = $this->divheight;\n                $save_sbd = $this->spanborddet;\n                $save_textvar = $this->textvar; // mPDF 5.7.1\n                $this->spanborddet = '';\n                $this->divheight = 0;\n                $this->textvar = 0x00; // mPDF 5.7.1\n\n                $this->Cell($w, $h, $dots, 0, 0, 'C', 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']); // mPDF 6 DOTTAB\n                $this->spanborddet = $save_sbd;\n                $this->textvar = $save_textvar; // mPDF 5.7.1\n                $this->divheight = $save_dh;\n                $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            }\n\n            /* -- FORMS -- */\n            // TEXT/PASSWORD INPUT\n            if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD')) {\n                $this->form->print_ob_text($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);\n            }\n\n            // TEXTAREA\n            if ($objattr['type'] == 'textarea') {\n                $this->form->print_ob_textarea($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);\n            }\n\n            // SELECT\n            if ($objattr['type'] == 'select') {\n                $this->form->print_ob_select($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);\n            }\n\n\n            // INPUT/BUTTON as IMAGE\n            if ($objattr['type'] == 'input' && $objattr['subtype'] == 'IMAGE') {\n                $this->form->print_ob_imageinput($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $is_table);\n            }\n\n            // BUTTON\n            if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'SUBMIT' || $objattr['subtype'] == 'RESET' || $objattr['subtype'] == 'BUTTON')) {\n                $this->form->print_ob_button($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);\n            }\n\n            // CHECKBOX\n            if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'CHECKBOX')) {\n                $this->form->print_ob_checkbox($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);\n            }\n            // RADIO\n            if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'RADIO')) {\n                $this->form->print_ob_radio($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);\n            }\n            /* -- END FORMS -- */\n        }\n\n        $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);\n\n        $this->y = $save_y;\n        $this->x = $save_x;\n\n        unset($content);\n    }\n\n    function _printListBullet($x, $y, $size, $type, $color)\n    {\n        // x and y are the centre of the bullet; size is the width and/or height in mm\n        $fcol = $this->SetTColor($color, true);\n        $lcol = strtoupper($fcol); // change 0 0 0 rg to 0 0 0 RG\n        $this->writer->write(sprintf('q %s %s', $lcol, $fcol));\n        $this->writer->write('0 j 0 J [] 0 d');\n        if ($type == 'square') {\n            $size *= 0.85; // Smaller to appear the same size as circle/disc\n            $this->writer->write(sprintf('%.3F %.3F %.3F %.3F re f', ($x - $size / 2) * Mpdf::SCALE, ($this->h - $y + $size / 2) * Mpdf::SCALE, ($size) * Mpdf::SCALE, (-$size) * Mpdf::SCALE));\n        } elseif ($type == 'disc') {\n            $this->Circle($x, $y, $size / 2, 'F'); // Fill\n        } elseif ($type == 'circle') {\n            $lw = $size / 12; // Line width\n            $this->writer->write(sprintf('%.3F w ', $lw * Mpdf::SCALE));\n            $this->Circle($x, $y, $size / 2 - $lw / 2, 'S'); // Stroke\n        }\n        $this->writer->write('Q');\n    }\n\n    // mPDF 6\n    // Get previous character and move pointers\n    function _moveToPrevChar(&$contentctr, &$charctr, $content)\n    {\n        $lastchar = false;\n        $charctr--;\n        while ($charctr < 0) { // go back to previous $content[]\n            $contentctr--;\n            if ($contentctr < 0) {\n                return false;\n            }\n            if ($this->usingCoreFont) {\n                $charctr = strlen($content[$contentctr]) - 1;\n            } else {\n                $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;\n            }\n        }\n        if ($this->usingCoreFont) {\n            $lastchar = $content[$contentctr][$charctr];\n        } else {\n            $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);\n        }\n        return $lastchar;\n    }\n\n    // Get previous character\n    function _getPrevChar($contentctr, $charctr, $content)\n    {\n        $lastchar = false;\n        $charctr--;\n        while ($charctr < 0) { // go back to previous $content[]\n            $contentctr--;\n            if ($contentctr < 0) {\n                return false;\n            }\n            if ($this->usingCoreFont) {\n                $charctr = strlen($content[$contentctr]) - 1;\n            } else {\n                $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;\n            }\n        }\n        if ($this->usingCoreFont) {\n            $lastchar = $content[$contentctr][$charctr];\n        } else {\n            $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);\n        }\n        return $lastchar;\n    }\n\n    function WriteFlowingBlock($s, $sOTLdata)\n    {\n        // mPDF 5.7.1\n        $currentx = $this->x;\n        $is_table = $this->flowingBlockAttr['is_table'];\n        $table_draft = $this->flowingBlockAttr['table_draft'];\n        // width of all the content so far in points\n        $contentWidth = & $this->flowingBlockAttr['contentWidth'];\n        // cell width in points\n        $maxWidth = & $this->flowingBlockAttr['width'];\n        $lineCount = & $this->flowingBlockAttr['lineCount'];\n        // line height in user units\n        $stackHeight = & $this->flowingBlockAttr['height'];\n        $align = & $this->flowingBlockAttr['align'];\n        $content = & $this->flowingBlockAttr['content'];\n        $contentB = & $this->flowingBlockAttr['contentB'];\n        $font = & $this->flowingBlockAttr['font'];\n        $valign = & $this->flowingBlockAttr['valign'];\n        $blockstate = $this->flowingBlockAttr['blockstate'];\n        $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1\n\n        $newblock = $this->flowingBlockAttr['newblock'];\n        $blockdir = $this->flowingBlockAttr['blockdir'];\n\n        // *********** BLOCK BACKGROUND COLOR ***************** //\n        if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {\n            $fill = 0;\n        } else {\n            $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n            $fill = 0;\n        }\n        $font[] = $this->saveFont();\n        $content[] = '';\n        $contentB[] = '';\n        $cOTLdata[] = $sOTLdata; // mPDF 5.7.1\n        $currContent = & $content[count($content) - 1];\n\n        $CJKoverflow = false;\n        $Oikomi = false; // mPDF 6\n        $hanger = '';\n\n        // COLS\n        $oldcolumn = $this->CurrCol;\n        if ($this->ColActive && !$is_table) {\n            $this->breakpoints[$this->CurrCol][] = $this->y;\n        } // *COLUMNS*\n\n        /* -- TABLES -- */\n        if ($is_table) {\n            $ipaddingL = 0;\n            $ipaddingR = 0;\n            $paddingL = 0;\n            $paddingR = 0;\n            $cpaddingadjustL = 0;\n            $cpaddingadjustR = 0;\n            // Added mPDF 3.0\n            $fpaddingR = 0;\n            $fpaddingL = 0;\n        } else {\n            /* -- END TABLES -- */\n            $ipaddingL = $this->blk[$this->blklvl]['padding_left'];\n            $ipaddingR = $this->blk[$this->blklvl]['padding_right'];\n            $paddingL = ($ipaddingL * Mpdf::SCALE);\n            $paddingR = ($ipaddingR * Mpdf::SCALE);\n            $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];\n            $cpaddingadjustL = -$this->cMarginL;\n            $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];\n            $cpaddingadjustR = -$this->cMarginR;\n            // Added mPDF 3.0 Float DIV\n            $fpaddingR = 0;\n            $fpaddingL = 0;\n            /* -- CSS-FLOAT -- */\n            if (count($this->floatDivs)) {\n                [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width] = $this->GetFloatDivInfo($this->blklvl);\n                if ($r_exists) {\n                    $fpaddingR = $r_width;\n                }\n                if ($l_exists) {\n                    $fpaddingL = $l_width;\n                }\n            }\n            /* -- END CSS-FLOAT -- */\n\n            $usey = $this->y + 0.002;\n            if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {\n                $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];\n            }\n            /* -- CSS-IMAGE-FLOAT -- */\n            // If float exists at this level\n            if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {\n                $fpaddingR += $this->floatmargins['R']['w'];\n            }\n            if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {\n                $fpaddingL += $this->floatmargins['L']['w'];\n            }\n            /* -- END CSS-IMAGE-FLOAT -- */\n        } // *TABLES*\n        // OBJECTS - IMAGES & FORM Elements (NB has already skipped line/page if required - in printbuffer)\n        if (substr($s, 0, 3) == \"\\xbb\\xa4\\xac\") { // identifier has been identified!\n            $objattr = $this->_getObjAttr($s);\n            $h_corr = 0;\n            if ($is_table) { // *TABLES*\n                $maximumW = ($maxWidth / Mpdf::SCALE) - ($this->cellPaddingL + $this->cMarginL + $this->cellPaddingR + $this->cMarginR);  // *TABLES*\n            } // *TABLES*\n            else { // *TABLES*\n                if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0) && (!$is_table)) {\n                    $h_corr = $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];\n                }\n                $maximumW = ($maxWidth / Mpdf::SCALE) - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w'] + $fpaddingL + $fpaddingR );\n            } // *TABLES*\n            $objattr = $this->inlineObject($objattr['type'], $this->lMargin + $fpaddingL + ($contentWidth / Mpdf::SCALE), ($this->y + $h_corr), $objattr, $this->lMargin, ($contentWidth / Mpdf::SCALE), $maximumW, $stackHeight, true, $is_table);\n\n            // SET LINEHEIGHT for this line ================ RESET AT END\n            $stackHeight = max($stackHeight, $objattr['OUTER-HEIGHT']);\n            $this->objectbuffer[count($content) - 1] = $objattr;\n            // if (isset($objattr['vertical-align'])) { $valign = $objattr['vertical-align']; }\n            // else { $valign = ''; }\n            // LIST MARKERS\t// mPDF 6  Lists\n            if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {\n                // do nothing\n            } else {\n                $contentWidth += ($objattr['OUTER-WIDTH'] * Mpdf::SCALE);\n            }\n            return;\n        }\n\n        $lbw = $rbw = 0; // Border widths\n        if (!empty($this->spanborddet)) {\n            if (isset($this->spanborddet['L'])) {\n                $lbw = $this->spanborddet['L']['w'];\n            }\n            if (isset($this->spanborddet['R'])) {\n                $rbw = $this->spanborddet['R']['w'];\n            }\n        }\n\n        if ($this->usingCoreFont) {\n            $clen = strlen($s);\n        } else {\n            $clen = mb_strlen($s, $this->mb_enc);\n        }\n\n        // for every character in the string\n        for ($i = 0; $i < $clen; $i++) {\n            // extract the current character\n            // get the width of the character in points\n            if ($this->usingCoreFont) {\n                $c = $s[$i];\n                // Soft Hyphens chr(173)\n                $cw = ($this->GetCharWidthCore($c) * Mpdf::SCALE);\n                if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1\n                    if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c])) {\n                        $cw += ($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c] * $this->FontSizePt / 1000 );\n                    }\n                }\n            } else {\n                $c = mb_substr($s, $i, 1, $this->mb_enc);\n                $cw = ($this->GetCharWidthNonCore($c, false) * Mpdf::SCALE);\n                // mPDF 5.7.1\n                // Use OTL GPOS\n                if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {\n                    // ...WriteFlowingBlock...\n                    // Only  add XAdvanceL (not sure at present whether RTL or LTR writing direction)\n                    // At this point, XAdvanceL and XAdvanceR will balance\n                    if (isset($sOTLdata['GPOSinfo'][$i]['XAdvanceL'])) {\n                        $cw += $sOTLdata['GPOSinfo'][$i]['XAdvanceL'] * (1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000) * Mpdf::SCALE;\n                    }\n                }\n                if (($this->textvar & TextVars::FC_KERNING) && $i > 0) { // mPDF 5.7.1\n                    $lastc = mb_substr($s, ($i - 1), 1, $this->mb_enc);\n                    $ulastc = $this->UTF8StringToArray($lastc, false);\n                    $uc = $this->UTF8StringToArray($c, false);\n                    if (isset($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]])) {\n                        $cw += ($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]] * $this->FontSizePt / 1000 );\n                    }\n                }\n            }\n\n            if ($i == 0) {\n                $cw += $lbw * Mpdf::SCALE;\n                $contentB[(count($contentB) - 1)] .= 'L';\n            }\n            if ($i == ($clen - 1)) {\n                $cw += $rbw * Mpdf::SCALE;\n                $contentB[(count($contentB) - 1)] .= 'R';\n            }\n            if ($c == ' ') {\n                $currContent .= $c;\n                $contentWidth += $cw;\n                continue;\n            }\n\n            // Paragraph INDENT\n            $WidthCorrection = 0;\n            if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {\n                $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4\n                $WidthCorrection = ($ti * Mpdf::SCALE);\n            }\n            // OUTDENT\n            foreach ($this->objectbuffer as $k => $objattr) {   // mPDF 6 DOTTAB\n                if ($objattr['type'] == 'dottab') {\n                    $WidthCorrection -= ($objattr['outdent'] * Mpdf::SCALE);\n                    break;\n                }\n            }\n\n\n            // Added mPDF 3.0 Float DIV\n            $fpaddingR = 0;\n            $fpaddingL = 0;\n            /* -- CSS-FLOAT -- */\n            if (count($this->floatDivs)) {\n                [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width] = $this->GetFloatDivInfo($this->blklvl);\n                if ($r_exists) {\n                    $fpaddingR = $r_width;\n                }\n                if ($l_exists) {\n                    $fpaddingL = $l_width;\n                }\n            }\n            /* -- END CSS-FLOAT -- */\n\n            $usey = $this->y + 0.002;\n            if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {\n                $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];\n            }\n\n            /* -- CSS-IMAGE-FLOAT -- */\n            // If float exists at this level\n            if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {\n                $fpaddingR += $this->floatmargins['R']['w'];\n            }\n            if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {\n                $fpaddingL += $this->floatmargins['L']['w'];\n            }\n            /* -- END CSS-IMAGE-FLOAT -- */\n\n\n            // try adding another char\n            if (( $contentWidth + $cw > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) + 0.001)) {// 0.001 is to correct for deviations converting mm=>pts\n                // it won't fit, output what we already have\n                $lineCount++;\n\n                // contains any content that didn't make it into this print\n                $savedContent = '';\n                $savedContentB = '';\n                $savedOTLdata = []; // mPDF 5.7.1\n                $savedFont = [];\n                $savedObj = [];\n                $savedPreOTLdata = []; // mPDF 5.7.1\n                $savedPreContent = [];\n                $savedPreContentB = [];\n                $savedPreFont = [];\n\n                // mPDF 6\n                // New line-breaking algorithm\n                /////////////////////\n                // LINE BREAKING\n                /////////////////////\n                $breakfound = false;\n                $contentctr = count($content) - 1;\n                if ($this->usingCoreFont) {\n                    $charctr = strlen($currContent);\n                } else {\n                    $charctr = mb_strlen($currContent, $this->mb_enc);\n                }\n                $checkchar = $c;\n                $prevchar = $this->_getPrevChar($contentctr, $charctr, $content);\n\n                /* -- CJK-FONTS -- */\n                // 1) CJK Overflowing a) punctuation or b) Oikomi\n                // Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi\n                if ($CJKoverflow || $Oikomi) { // If flag already set\n                    $CJKoverflow = false;\n                    $Oikomi = false;\n                    $breakfound = true;\n                }\n                if (!$this->usingCoreFont && !$breakfound && $this->checkCJK) {\n\n                    // Get next/following character (in this chunk)\n                    $followingchar = '';\n                    if ($i < ($clen - 1)) {\n                        if ($this->usingCoreFont) {\n                            $followingchar = $s[$i + 1];\n                        } else {\n                            $followingchar = mb_substr($s, $i + 1, 1, $this->mb_enc);\n                        }\n                    }\n\n                    // 1a) Overflow punctuation\n                    if (preg_match(\"/[\" . $this->pregCJKchars . \"]/u\", $prevchar) && preg_match(\"/[\" . $this->CJKoverflow . \"]/u\", $checkchar) && $this->allowCJKorphans) {\n                        // add character onto this line\n                        $currContent .= $c;\n                        $contentWidth += $cw;\n                        $CJKoverflow = true; // Set flag\n                        continue;\n                    } elseif (preg_match(\"/[\" . $this->pregCJKchars . \"]/u\", $checkchar) && $this->allowCJKorphans &&\n                        (preg_match(\"/[\" . $this->CJKleading . \"]/u\", $followingchar) || preg_match(\"/[\" . $this->CJKfollowing . \"]/u\", $checkchar)) &&\n                        !preg_match(\"/[\" . $this->CJKleading . \"]/u\", $checkchar) && !preg_match(\"/[\" . $this->CJKfollowing . \"]/u\", $followingchar) &&\n                        !(preg_match(\"/[0-9\\x{ff10}-\\x{ff19}]/u\", $followingchar) && preg_match(\"/[0-9\\x{ff10}-\\x{ff19}]/u\", $checkchar))) {\n                        // 1b) Try squeezing another character(s) onto this line = Oikomi, if character cannot end line\n                        // or next character cannot start line (and not splitting CJK numerals)\n                        // NB otherwise it move lastchar(s) to next line to keep $c company = Oidashi, which is done below in standard way\n                        // add character onto this line\n                        $currContent .= $c;\n                        $contentWidth += $cw;\n                        $Oikomi = true; // Set flag\n                        continue;\n                    }\n                }\n                /* -- END CJK-FONTS -- */\n                /* -- HYPHENATION -- */\n\n                // AUTOMATIC HYPHENATION\n                // 2) Automatic hyphen in current word (does not cross tags)\n                if (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] == 1) {\n                    $currWord = '';\n                    // Look back and ahead to get current word\n                    for ($ac = $charctr - 1; $ac >= 0; $ac--) {\n                        if ($this->usingCoreFont) {\n                            $addc = substr($currContent, $ac, 1);\n                        } else {\n                            $addc = mb_substr($currContent, $ac, 1, $this->mb_enc);\n                        }\n                        if ($addc == ' ') {\n                            break;\n                        }\n                        $currWord = $addc . $currWord;\n                    }\n                    $start = $ac + 1;\n                    for ($ac = $i; $ac < ($clen - 1); $ac++) {\n                        if ($this->usingCoreFont) {\n                            $addc = substr($s, $ac, 1);\n                        } else {\n                            $addc = mb_substr($s, $ac, 1, $this->mb_enc);\n                        }\n                        if ($addc == ' ') {\n                            break;\n                        }\n                        $currWord .= $addc;\n                    }\n                    $ptr = $this->hyphenator->hyphenateWord($currWord, $charctr - $start);\n                    if ($ptr > -1) {\n                        $breakfound = [$contentctr, $start + $ptr, $contentctr, $start + $ptr, 'hyphen'];\n                    }\n                }\n                /* -- END HYPHENATION -- */\n\n                // Search backwards to find first line-break opportunity\n                while ($breakfound == false && $prevchar !== false) {\n                    $cutcontentctr = $contentctr;\n                    $cutcharctr = $charctr;\n                    $prevchar = $this->_moveToPrevChar($contentctr, $charctr, $content);\n                    /////////////////////\n                    // 3) Break at SPACE\n                    /////////////////////\n                    if ($prevchar == ' ') {\n                        $breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];\n                    } /////////////////////\n                    // 4) Break at U+200B in current word (Khmer, Lao & Thai Invisible word boundary, and Tibetan)\n                    /////////////////////\n                    elseif ($prevchar == \"\\xe2\\x80\\x8b\") { // U+200B Zero-width Word Break\n                        $breakfound = [$contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard'];\n                    } /////////////////////\n                    // 5) Break at Hard HYPHEN '-' or U+2010\n                    /////////////////////\n                    elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && ($prevchar == '-' || $prevchar == \"\\xe2\\x80\\x90\")) {\n                        // Don't break a URL\n                        // Look back to get first part of current word\n                        $checkw = '';\n                        for ($ac = $charctr - 1; $ac >= 0; $ac--) {\n                            if ($this->usingCoreFont) {\n                                $addc = substr($currContent, $ac, 1);\n                            } else {\n                                $addc = mb_substr($currContent, $ac, 1, $this->mb_enc);\n                            }\n                            if ($addc == ' ') {\n                                break;\n                            }\n                            $checkw = $addc . $checkw;\n                        }\n                        // Don't break if HyphenMinus AND (a URL or before a numeral or before a >)\n                        if ((!preg_match('/(http:|ftp:|https:|www\\.)/', $checkw) && $checkchar != '>' && !preg_match('/[0-9]/', $checkchar)) || $prevchar == \"\\xe2\\x80\\x90\") {\n                            $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];\n                        }\n                    } /////////////////////\n                    // 6) Break at Soft HYPHEN (replace with hard hyphen)\n                    /////////////////////\n                    elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && !$this->usingCoreFont && $prevchar == \"\\xc2\\xad\") {\n                        $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];\n                        $content[$contentctr] = mb_substr($content[$contentctr], 0, $charctr, $this->mb_enc) . '-' . mb_substr($content[$contentctr], $charctr + 1, mb_strlen($content[$contentctr]), $this->mb_enc);\n                        if (!empty($cOTLdata[$contentctr])) {\n                            $cOTLdata[$contentctr]['char_data'][$charctr] = ['bidi_class' => 9, 'uni' => 45];\n                            $cOTLdata[$contentctr]['group'][$charctr] = 'C';\n                        }\n                    } elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && $prevchar == chr(173)) {\n                        $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];\n                        $content[$contentctr] = substr($content[$contentctr], 0, $charctr) . '-' . substr($content[$contentctr], $charctr + 1);\n                    } /* -- CJK-FONTS -- */\n                    /////////////////////\n                    // 7) Break at CJK characters (unless forbidden characters to end or start line)\n                    // CJK Avoiding line break in the middle of numerals\n                    /////////////////////\n                    elseif (!$this->usingCoreFont && $this->checkCJK && preg_match(\"/[\" . $this->pregCJKchars . \"]/u\", $checkchar) &&\n                        !preg_match(\"/[\" . $this->CJKfollowing . \"]/u\", $checkchar) && !preg_match(\"/[\" . $this->CJKleading . \"]/u\", $prevchar) &&\n                        !(preg_match(\"/[0-9\\x{ff10}-\\x{ff19}]/u\", $prevchar) && preg_match(\"/[0-9\\x{ff10}-\\x{ff19}]/u\", $checkchar))) {\n                        $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];\n                    }\n                    /* -- END CJK-FONTS -- */\n                    /////////////////////\n                    // 8) Break at OBJECT (Break before all objects here - selected objects are moved forward to next line below e.g. dottab)\n                    /////////////////////\n                    if (isset($this->objectbuffer[$contentctr])) {\n                        $breakfound = [$cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut'];\n                    }\n\n\n                    $checkchar = $prevchar;\n                }\n\n                // If a line-break opportunity found:\n                if (is_array($breakfound)) {\n                    $contentctr = $breakfound[0];\n                    $charctr = $breakfound[1];\n                    $cutcontentctr = $breakfound[2];\n                    $cutcharctr = $breakfound[3];\n                    $type = $breakfound[4];\n                    // Cache chunks which are already processed, but now need to be passed on to the new line\n                    for ($ix = count($content) - 1; $ix > $cutcontentctr; $ix--) {\n                        // save and crop off any subsequent chunks\n                        /* -- OTL -- */\n                        if (!empty($sOTLdata)) {\n                            $tmpOTL = array_pop($cOTLdata);\n                            $savedPreOTLdata[] = $tmpOTL;\n                        }\n                        /* -- END OTL -- */\n                        $savedPreContent[] = array_pop($content);\n                        $savedPreContentB[] = array_pop($contentB);\n                        $savedPreFont[] = array_pop($font);\n                    }\n\n                    // Next cache the part which will start the next line\n                    if ($this->usingCoreFont) {\n                        $savedPreContent[] = substr($content[$cutcontentctr], $cutcharctr);\n                    } else {\n                        $savedPreContent[] = mb_substr($content[$cutcontentctr], $cutcharctr, mb_strlen($content[$cutcontentctr]), $this->mb_enc);\n                    }\n                    $savedPreContentB[] = preg_replace('/L/', '', $contentB[$cutcontentctr]);\n                    $savedPreFont[] = $font[$cutcontentctr];\n                    /* -- OTL -- */\n                    if (!empty($sOTLdata)) {\n                        $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[$cutcontentctr], $cutcharctr, $cutcharctr);\n                    }\n                    /* -- END OTL -- */\n\n\n                    // Finally adjust the Current content which ends this line\n                    if ($cutcharctr == 0 && $type == 'discard') {\n                        array_pop($content);\n                        array_pop($contentB);\n                        array_pop($font);\n                        array_pop($cOTLdata);\n                    }\n\n                    $currContent = & $content[count($content) - 1];\n                    if ($this->usingCoreFont) {\n                        $currContent = substr($currContent, 0, $charctr);\n                    } else {\n                        $currContent = mb_substr($currContent, 0, $charctr, $this->mb_enc);\n                    }\n\n                    if (!empty($sOTLdata)) {\n                        $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));\n                    }\n\n                    if (strpos($contentB[(count($contentB) - 1)], 'R') !== false) {   // ???\n                        $contentB[count($content) - 1] = preg_replace('/R/', '', $contentB[count($content) - 1]); // ???\n                    }\n\n                    if ($type == 'hyphen') {\n                        $currContent .= '-';\n                        if (!empty($cOTLdata[(count($cOTLdata) - 1)])) {\n                            $cOTLdata[(count($cOTLdata) - 1)]['char_data'][] = ['bidi_class' => 9, 'uni' => 45];\n                            $cOTLdata[(count($cOTLdata) - 1)]['group'] .= 'C';\n                        }\n                    }\n\n                    $savedContent = '';\n                    $savedContentB = '';\n                    $savedFont = [];\n                    $savedOTLdata = [];\n                }\n                // If no line-break opportunity found - split at current position\n                // or - Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi, as set above by:\n                // 1) CJK Overflowing a) punctuation or b) Oikomi\n                // in which case $breakfound==1 and NOT array\n\n                if (!is_array($breakfound)) {\n                    $savedFont = $this->saveFont();\n                    if (!empty($sOTLdata)) {\n                        $savedOTLdata = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));\n                    }\n                }\n\n                if ($content[count($content) - 1] == '' && !isset($this->objectbuffer[count($content) - 1])) {\n                    array_pop($content);\n                    array_pop($contentB);\n                    array_pop($font);\n                    array_pop($cOTLdata);\n                    $currContent = & $content[count($content) - 1];\n                }\n\n                // Right Trim current content - including CJK space, and for OTLdata\n                // incl. CJK - strip CJK space at end of line &#x3000; = \\xe3\\x80\\x80 = CJK space\n                $currContent = rtrim($currContent);\n                if ($this->checkCJK) {\n                    $currContent = preg_replace(\"/\\xe3\\x80\\x80$/\", '', $currContent);\n                } // *CJK-FONTS*\n                /* -- OTL -- */\n                if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                    $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true); // NB also does U+3000\n                }\n                /* -- END OTL -- */\n\n\n                // Selected OBJECTS are moved forward to next line, unless they come before a space or U+200B (type='discard')\n                if (isset($this->objectbuffer[(count($content) - 1)]) && (!isset($type) || $type != 'discard')) {\n                    $objtype = $this->objectbuffer[(count($content) - 1)]['type'];\n                    if ($objtype == 'dottab' || $objtype == 'bookmark' || $objtype == 'indexentry' || $objtype == 'toc' || $objtype == 'annot') {\n                        $savedObj = array_pop($this->objectbuffer);\n                    }\n                }\n\n\n                // Decimal alignment (cancel if wraps to > 1 line)\n                if ($is_table && substr($align, 0, 1) == 'D') {\n                    $align = substr($align, 2, 1);\n                }\n\n                $lineBox = [];\n\n                $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);\n\n                // update $contentWidth since it has changed with cropping\n                $contentWidth = 0;\n\n                $inclCursive = false;\n                foreach ($content as $k => $chunk) {\n                    if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {\n                        // LIST MARKERS\n                        if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker']) {\n                            if ($this->objectbuffer[$k]['listmarkerposition'] != 'outside') {\n                                $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;\n                            }\n                        } else {\n                            $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * Mpdf::SCALE;\n                        }\n                    } elseif (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {\n                        $this->restoreFont($font[$k], false);\n                        if ($this->checkCJK && $k == count($content) - 1 && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $this->CJKforceend) {\n                            // force-end overhang\n                            $hanger = mb_substr($chunk, mb_strlen($chunk, $this->mb_enc) - 1, 1, $this->mb_enc);\n                            // Probably ought to do something with char_data and GPOS in cOTLdata...\n                            $content[$k] = $chunk = mb_substr($chunk, 0, mb_strlen($chunk, $this->mb_enc) - 1, $this->mb_enc);\n                        }\n\n                        // Soft Hyphens chr(173) + Replace NBSP with SPACE + Set inclcursive if includes CURSIVE TEXT\n                        if (!$this->usingCoreFont) {\n                            /* -- OTL -- */\n                            if ((isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) || !empty($sOTLdata)) {\n                                $this->otl->removeChar($chunk, $cOTLdata[$k], \"\\xc2\\xad\");\n                                $this->otl->replaceSpace($chunk, $cOTLdata[$k]); // NBSP -> space\n                                if (preg_match(\"/([\" . $this->pregCURSchars . \"])/u\", $chunk)) {\n                                    $inclCursive = true;\n                                }\n                                $content[$k] = $chunk;\n                            } /* -- END OTL -- */ else {  // *OTL*\n                                $content[$k] = $chunk = str_replace(\"\\xc2\\xad\", '', $chunk);\n                                $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);\n                            } // *OTL*\n                        } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {\n                            $content[$k] = $chunk = str_replace(chr(173), '', $chunk);\n                            $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);\n                        }\n\n                        $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * Mpdf::SCALE;  // mPDF 5.7.1\n                        if (!empty($this->spanborddet)) {\n                            if (isset($this->spanborddet['L']['w']) && strpos($contentB[$k], 'L') !== false) {\n                                $contentWidth += $this->spanborddet['L']['w'] * Mpdf::SCALE;\n                            }\n                            if (isset($this->spanborddet['R']['w']) && strpos($contentB[$k], 'R') !== false) {\n                                $contentWidth += $this->spanborddet['R']['w'] * Mpdf::SCALE;\n                            }\n                        }\n                    }\n                }\n\n                $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');\n                $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');\n                if ($blockdir == 'ltr' && strpos($lastfontreqstyle, \"I\") !== false && strpos($lastfontstyle, \"I\") === false) { // Artificial italic\n                    $lastitalic = $this->FontSize * 0.15 * Mpdf::SCALE;\n                } else {\n                    $lastitalic = 0;\n                }\n\n\n\n\n                // NOW FORMAT THE LINE TO OUTPUT\n                if (!$table_draft) {\n                    // DIRECTIONALITY RTL\n                    $chunkorder = range(0, count($content) - 1); // mPDF 5.7\n                    /* -- OTL -- */\n                    // mPDF 6\n                    if ($blockdir == 'rtl' || $this->biDirectional) {\n                        $this->otl->bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);\n                        // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to\n                        // $this->objectbuffer and $font ($chunkorder contains the mapping)\n                    }\n\n                    /* -- END OTL -- */\n                    // Remove any XAdvance from OTL data at end of line\n                    foreach ($chunkorder as $aord => $k) {\n                        if (count($cOTLdata)) {\n                            $this->restoreFont($font[$k], false);\n                            // ...WriteFlowingBlock...\n                            if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line\n                                $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character\n                                if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {\n                                    if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {\n                                        $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                                    } else {\n                                        $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                                    }\n                                    $w *= ($this->FontSize / 1000);\n                                    $contentWidth -= $w * Mpdf::SCALE;\n                                    $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;\n                                    $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;\n                                }\n\n                                // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it\n                                if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {\n                                    $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];\n                                    $w *= ($this->FontSize / 1000);\n                                    $contentWidth -= $w * Mpdf::SCALE;\n                                    $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];\n                                    $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];\n                                }\n                            }\n                        }\n                    }\n\n                    // JUSTIFICATION J\n                    $jcharspacing = 0;\n                    $jws = 0;\n                    $nb_carac = 0;\n                    $nb_spaces = 0;\n                    $jkashida = 0;\n                    // if it's justified, we need to find the char/word spacing (or if hanger $this->CJKforceend)\n                    if (($align == 'J' && !$CJKoverflow) || (($contentWidth + $lastitalic > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) ) + 0.001) && (!$CJKoverflow || ($CJKoverflow && !$this->allowCJKoverflow))) || $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {   // 0.001 is to correct for deviations converting mm=>pts\n                        // JUSTIFY J (character spacing)\n                        // WORD SPACING\n                        // mPDF 5.7\n                        foreach ($chunkorder as $aord => $k) {\n                            $chunk = isset($content[$aord]) ? $content[$aord] : '';\n                            if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {\n                                $nb_carac += mb_strlen($chunk, $this->mb_enc);\n                                $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);\n                                // GPOS OTL\n                                if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {\n                                    if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {\n                                        $nb_carac -= substr_count($cOTLdata[$aord]['group'], 'M');\n                                    }\n                                }\n                            } else {\n                                $nb_carac ++;\n                            } // mPDF 6 allow spacing for inline object\n                        }\n                        // GetJSpacing adds kashida spacing to GPOSinfo if appropriate for Font\n                        [$jcharspacing, $jws, $jkashida] = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) )), $inclCursive, $cOTLdata);\n                    }\n\n                    // WORD SPACING\n                    $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * Mpdf::SCALE) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * Mpdf::SCALE) );\n\n                    $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1\n                    $empty -= ($jws * $nb_spaces);\n                    $empty -= ($jkashida);\n                    $empty /= Mpdf::SCALE;\n\n                    $b = ''; // do not borders\n                    // Get PAGEBREAK TO TEST for height including the top border/padding\n                    $check_h = max($this->divheight, $stackHeight);\n                    if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blklvl > 0) && ($lineCount == 1) && (!$is_table)) {\n                        $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);\n                    }\n\n                    if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {\n                        $this->SetCol($this->NbCol - 1);\n                    }\n\n                    // PAGEBREAK\n                    // 'If' below used in order to fix \"first-line of other page with justify on\" bug\n                    if (!$is_table && ($this->y + $check_h) > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {\n                        $bak_x = $this->x; // Current X position\n                        // WORD SPACING\n                        $ws = $this->ws; // Word Spacing\n                        $charspacing = $this->charspacing; // Character Spacing\n                        $this->ResetSpacing();\n\n                        $this->AddPage($this->CurOrientation);\n\n                        $this->x = $bak_x;\n                        // Added to correct for OddEven Margins\n                        $currentx += $this->MarginCorrection;\n                        $this->x += $this->MarginCorrection;\n\n                        // WORD SPACING\n                        $this->SetSpacing($charspacing, $ws);\n                    }\n\n                    if ($this->kwt && !$is_table) { // mPDF 5.7+\n                        $this->printkwtbuffer();\n                        $this->kwt = false;\n                    }\n\n\n                    /* -- COLUMNS -- */\n                    // COLS\n                    // COLUMN CHANGE\n                    if ($this->CurrCol != $oldcolumn) {\n                        $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n                        $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n                        $oldcolumn = $this->CurrCol;\n                    }\n\n                    if ($this->ColActive && !$is_table) {\n                        $this->breakpoints[$this->CurrCol][] = $this->y;\n                    } // *COLUMNS*\n                    /* -- END COLUMNS -- */\n\n                    // TOP MARGIN\n                    if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && ($lineCount == 1) && (!$is_table)) {\n                        $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);\n                        if ($this->ColActive) {\n                            $this->breakpoints[$this->CurrCol][] = $this->y;\n                        } // *COLUMNS*\n                    }\n\n\n                    // Update y0 for top of block (used to paint border)\n                    if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table)) {\n                        $this->blk[$this->blklvl]['y0'] = $this->y;\n                        $this->blk[$this->blklvl]['startpage'] = $this->page;\n                        if ($this->blk[$this->blklvl]['float']) {\n                            $this->blk[$this->blklvl]['float_start_y'] = $this->y;\n                        }\n                    }\n\n                    // TOP PADDING and BORDER spacing/fill\n                    if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 1) && (!$is_table)) {\n                        // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom\n                        $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);\n                        if ($this->ColActive) {\n                            $this->breakpoints[$this->CurrCol][] = $this->y;\n                        } // *COLUMNS*\n                    }\n\n                    $arraysize = count($chunkorder);\n\n                    $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );\n\n                    // PAINT BACKGROUND FOR THIS LINE\n                    if (!$is_table) {\n                        $this->DivLn($stackHeight, $this->blklvl, false);\n                    } // false -> don't advance y\n\n                    $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;\n                    if ($align == 'R') {\n                        $this->x += $empty;\n                    } elseif ($align == 'C') {\n                        $this->x += ($empty / 2);\n                    }\n\n                    // Paragraph INDENT\n                    if (isset($this->blk[$this->blklvl]['text_indent']) && ($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table) && ($blockdir != 'rtl') && ($align != 'C')) {\n                        $ti = $this->sizeConverter->convert($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false);  // mPDF 5.7.4\n                        $this->x += $ti;\n                    }\n\n                    // BIDI magic_reverse moved upwards from here\n                    foreach ($chunkorder as $aord => $k) { // mPDF 5.7\n\n                        $chunk = isset($content[$aord]) ? $content[$aord] : '';\n\n                        if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {\n                            $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];\n                            $this->objectbuffer[$k]['OUTER-X'] += $xadj;\n                            $this->objectbuffer[$k]['BORDER-X'] += $xadj;\n                            $this->objectbuffer[$k]['INNER-X'] += $xadj;\n\n                            if ($this->objectbuffer[$k]['type'] == 'listmarker') {\n                                $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin\n                            }\n                            $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];\n                            if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB\n                                $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin\n                            }\n                            if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB\n                                $yadj += $lineBox[$k]['top'];\n                            }\n                            $this->objectbuffer[$k]['OUTER-Y'] += $yadj;\n                            $this->objectbuffer[$k]['BORDER-Y'] += $yadj;\n                            $this->objectbuffer[$k]['INNER-Y'] += $yadj;\n                        }\n\n                        $this->restoreFont($font[$k]);  // mPDF 5.7\n\n                        $this->SetSpacing(($this->fixedlSpacing * Mpdf::SCALE) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * Mpdf::SCALE + $jws);\n                        // Now unset these values so they don't influence GetStringwidth below or in fn. Cell\n                        $this->fixedlSpacing = false;\n                        $this->minwSpacing = 0;\n\n                        $save_vis = $this->visibility;\n                        if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {\n                            $this->SetVisibility($this->textparam['visibility']);\n                        }\n                        // *********** SPAN BACKGROUND COLOR ***************** //\n                        if ($this->spanbgcolor) {\n                            $cor = $this->spanbgcolorarray;\n                            $this->SetFColor($cor);\n                            $save_fill = $fill;\n                            $spanfill = 1;\n                            $fill = 1;\n                        }\n                        if (!empty($this->spanborddet)) {\n                            if (strpos($contentB[$k], 'L') !== false) {\n                                $this->x += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);\n                            }\n                            if (strpos($contentB[$k], 'L') === false) {\n                                $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;\n                            }\n                            if (strpos($contentB[$k], 'R') === false) {\n                                $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;\n                            }\n                        }\n\n                        // WORD SPACING\n                        // StringWidth this time includes any kashida spacing\n                        $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, true);\n\n                        $nch = mb_strlen($chunk, $this->mb_enc);\n                        // GPOS OTL\n                        if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {\n                            if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {\n                                $nch -= substr_count($cOTLdata[$aord]['group'], 'M');\n                            }\n                        }\n                        $stringWidth += ( $this->charspacing * $nch / Mpdf::SCALE );\n\n                        $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / Mpdf::SCALE );\n\n                        if (isset($this->objectbuffer[$k])) {\n                            // LIST MARKERS\t// mPDF 6  Lists\n                            if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {\n                                $stringWidth = 0;\n                            } else {\n                                $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];\n                            }\n                        }\n\n                        if ($stringWidth == 0) {\n                            $stringWidth = 0.000001;\n                        }\n\n                        if ($aord == $arraysize - 1) {\n                            $stringWidth -= ( $this->charspacing / Mpdf::SCALE );\n                            if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {\n                                // force-end overhang\n                                $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));\n                                $this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));\n                            } else {\n                                $this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mono-style line or last part (skips line)\n                            }\n                        } else {\n                            $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // first or middle part\n                        }\n\n                        if (!empty($this->spanborddet)) {\n                            if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1) {\n                                $this->x += $this->spanborddet['R']['w'];\n                            }\n                        }\n                        // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //\n                        if (isset($spanfill) && $spanfill) {\n                            $fill = $save_fill;\n                            $spanfill = 0;\n                            if ($fill) {\n                                $this->SetFColor($bcor);\n                            }\n                        }\n                        if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {\n                            $this->SetVisibility($save_vis);\n                        }\n                    }\n                } elseif ($table_draft) {\n                    $this->y += $stackHeight;\n                }\n\n                if (!$is_table) {\n                    $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin']));\n                    $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin']));\n                }\n\n                // move on to the next line, reset variables, tack on saved content and current char\n\n                if (!$table_draft) {\n                    $this->printobjectbuffer($is_table, $blockdir);\n                }\n                $this->objectbuffer = [];\n\n\n                /* -- CSS-IMAGE-FLOAT -- */\n                // Update values if set to skipline\n                if ($this->floatmargins) {\n                    $this->_advanceFloatMargins();\n                }\n                /* -- END CSS-IMAGE-FLOAT -- */\n\n                // Reset lineheight\n                $stackHeight = $this->divheight;\n                $valign = 'M';\n\n                $font = [];\n                $content = [];\n                $contentB = [];\n                $cOTLdata = []; // mPDF 5.7.1\n                $contentWidth = 0;\n                if (!empty($savedObj)) {\n                    $this->objectbuffer[] = $savedObj;\n                    $font[] = $savedFont;\n                    $content[] = '';\n                    $contentB[] = '';\n                    $cOTLdata[] = []; // mPDF 5.7.1\n                    $contentWidth += $savedObj['OUTER-WIDTH'] * Mpdf::SCALE;\n                }\n                if (count($savedPreContent) > 0) {\n                    for ($ix = count($savedPreContent) - 1; $ix >= 0; $ix--) {\n                        $font[] = $savedPreFont[$ix];\n                        $content[] = $savedPreContent[$ix];\n                        $contentB[] = $savedPreContentB[$ix];\n                        if (!empty($sOTLdata)) {\n                            $cOTLdata[] = $savedPreOTLdata[$ix];\n                        }\n                        $this->restoreFont($savedPreFont[$ix]);\n                        $lbw = $rbw = 0; // Border widths\n                        if (!empty($this->spanborddet)) {\n                            $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);\n                            $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);\n                        }\n                        if ($ix > 0) {\n                            $contentWidth += $this->GetStringWidth($savedPreContent[$ix], true, (isset($savedPreOTLdata[$ix]) ? $savedPreOTLdata[$ix] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1\n                            if (strpos($savedPreContentB[$ix], 'L') !== false) {\n                                $contentWidth += $lbw;\n                            }\n                            if (strpos($savedPreContentB[$ix], 'R') !== false) {\n                                $contentWidth += $rbw;\n                            }\n                        }\n                    }\n                    $savedPreContent = [];\n                    $savedPreContentB = [];\n                    $savedPreOTLdata = []; // mPDF 5.7.1\n                    $savedPreFont = [];\n                    $content[(count($content) - 1)] .= $c;\n                } else {\n                    $font[] = $savedFont;\n                    $content[] = $savedContent . $c;\n                    $contentB[] = $savedContentB;\n                    $cOTLdata[] = $savedOTLdata; // mPDF 5.7.1\n                }\n\n                $currContent = & $content[(count($content) - 1)];\n                $this->restoreFont($font[(count($font) - 1)]); // mPDF 6.0\n\n                /* -- CJK-FONTS -- */\n                // CJK - strip CJK space at start of line\n                // &#x3000; = \\xe3\\x80\\x80 = CJK space\n                if ($this->checkCJK && $currContent == \"\\xe3\\x80\\x80\") {\n                    $currContent = '';\n                    if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n                        $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], true, false); // left trim U+3000\n                    }\n                }\n                /* -- END CJK-FONTS -- */\n\n                $lbw = $rbw = 0; // Border widths\n                if (!empty($this->spanborddet)) {\n                    $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);\n                    $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);\n                }\n\n                $contentWidth += $this->GetStringWidth($currContent, false, (isset($cOTLdata[(count($cOTLdata) - 1)]) ? $cOTLdata[(count($cOTLdata) - 1)] : false), $this->textvar) * Mpdf::SCALE; // mPDF 5.7.1\n                if (strpos($savedContentB, 'L') !== false) {\n                    $contentWidth += $lbw;\n                }\n                $CJKoverflow = false;\n                $hanger = '';\n            } // another character will fit, so add it on\n            else {\n                $contentWidth += $cw;\n                $currContent .= $c;\n            }\n        }\n\n        unset($content);\n        unset($contentB);\n    }\n\n    // ----------------------END OF FLOWING BLOCK------------------------------------//\n\n\n    /* -- CSS-IMAGE-FLOAT -- */\n    // Update values if set to skipline\n    function _advanceFloatMargins()\n    {\n        // Update floatmargins - L\n        if (isset($this->floatmargins['L']) && $this->floatmargins['L']['skipline'] && $this->floatmargins['L']['y0'] != $this->y) {\n            $yadj = $this->y - $this->floatmargins['L']['y0'];\n            $this->floatmargins['L']['y0'] = $this->y;\n            $this->floatmargins['L']['y1'] += $yadj;\n\n            // Update objattr in floatbuffer\n            if ($this->floatbuffer[$this->floatmargins['L']['id']]['border_left']['w']) {\n                $this->floatbuffer[$this->floatmargins['L']['id']]['BORDER-Y'] += $yadj;\n            }\n            $this->floatbuffer[$this->floatmargins['L']['id']]['INNER-Y'] += $yadj;\n            $this->floatbuffer[$this->floatmargins['L']['id']]['OUTER-Y'] += $yadj;\n\n            // Unset values\n            $this->floatbuffer[$this->floatmargins['L']['id']]['skipline'] = false;\n            $this->floatmargins['L']['skipline'] = false;\n            $this->floatmargins['L']['id'] = '';\n        }\n        // Update floatmargins - R\n        if (isset($this->floatmargins['R']) && $this->floatmargins['R']['skipline'] && $this->floatmargins['R']['y0'] != $this->y) {\n            $yadj = $this->y - $this->floatmargins['R']['y0'];\n            $this->floatmargins['R']['y0'] = $this->y;\n            $this->floatmargins['R']['y1'] += $yadj;\n\n            // Update objattr in floatbuffer\n            if ($this->floatbuffer[$this->floatmargins['R']['id']]['border_left']['w']) {\n                $this->floatbuffer[$this->floatmargins['R']['id']]['BORDER-Y'] += $yadj;\n            }\n            $this->floatbuffer[$this->floatmargins['R']['id']]['INNER-Y'] += $yadj;\n            $this->floatbuffer[$this->floatmargins['R']['id']]['OUTER-Y'] += $yadj;\n\n            // Unset values\n            $this->floatbuffer[$this->floatmargins['R']['id']]['skipline'] = false;\n            $this->floatmargins['R']['skipline'] = false;\n            $this->floatmargins['R']['id'] = '';\n        }\n    }\n\n    /* -- END CSS-IMAGE-FLOAT -- */\n\n\n\n    /* -- END HTML-CSS -- */\n\n    function _SetTextRendering($mode)\n    {\n        if (!(($mode == 0) || ($mode == 1) || ($mode == 2))) {\n            throw new \\Mpdf\\MpdfException(\"Text rendering mode should be 0, 1 or 2 (value : $mode)\");\n        }\n        $tr = ($mode . ' Tr');\n        if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {\n            $this->writer->write($tr);\n        }\n        $this->pageoutput[$this->page]['TextRendering'] = $tr;\n    }\n\n    function SetTextOutline($params = [])\n    {\n        if (isset($params['outline-s']) && $params['outline-s']) {\n            $this->SetLineWidth($params['outline-WIDTH']);\n            $this->SetDColor($params['outline-COLOR']);\n            $tr = ('2 Tr');\n            if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {\n                $this->writer->write($tr);\n            }\n            $this->pageoutput[$this->page]['TextRendering'] = $tr;\n        } else { // Now resets all values\n            $this->SetLineWidth(0.2);\n            $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            $this->_SetTextRendering(0);\n            $tr = ('0 Tr');\n            if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {\n                $this->writer->write($tr);\n            }\n            $this->pageoutput[$this->page]['TextRendering'] = $tr;\n        }\n    }\n\n    function Image($file, $x, $y, $w = 0, $h = 0, $type = '', $link = '', $paint = true, $constrain = true, $watermark = false, $shownoimg = true, $allowvector = true)\n    {\n        $orig_srcpath = $file;\n        $this->GetFullPath($file);\n\n        $info = $this->imageProcessor->getImage($file, true, $allowvector, $orig_srcpath);\n        if (!$info && $paint) {\n            $info = $this->imageProcessor->getImage($this->noImageFile);\n            if ($info) {\n                $file = $this->noImageFile;\n                $w = ($info['w'] * (25.4 / $this->img_dpi));  // 14 x 16px\n                $h = ($info['h'] * (25.4 / $this->img_dpi));  // 14 x 16px\n            }\n        }\n        if (!$info) {\n            return false;\n        }\n        // Automatic width and height calculation if needed\n        if ($w == 0 and $h == 0) {\n            /* -- IMAGES-WMF -- */\n            if ($info['type'] == 'wmf') {\n                // WMF units are twips (1/20pt)\n                // divide by 20 to get points\n                // divide by k to get user units\n                $w = abs($info['w']) / (20 * Mpdf::SCALE);\n                $h = abs($info['h']) / (20 * Mpdf::SCALE);\n            } else { \t\t\t/* -- END IMAGES-WMF -- */\n                if ($info['type'] == 'svg') {\n                    // returned SVG units are pts\n                    // divide by k to get user units (mm)\n                    $w = abs($info['w']) / Mpdf::SCALE;\n                    $h = abs($info['h']) / Mpdf::SCALE;\n                } else {\n                    // Put image at default image dpi\n                    $w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);\n                    $h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);\n                }\n            }\n        }\n        if ($w == 0) {\n            $w = abs($h * $info['w'] / $info['h']);\n        }\n        if ($h == 0) {\n            $h = abs($w * $info['h'] / $info['w']);\n        }\n\n        /* -- WATERMARK -- */\n        if ($watermark) {\n            $maxw = $this->w;\n            $maxh = $this->h;\n            // Size = D PF or array\n            if (is_array($this->watermark_size)) {\n                $w = $this->watermark_size[0];\n                $h = $this->watermark_size[1];\n            } elseif (!is_string($this->watermark_size)) {\n                $maxw -= $this->watermark_size * 2;\n                $maxh -= $this->watermark_size * 2;\n                $w = $maxw;\n                $h = abs($w * $info['h'] / $info['w']);\n                if ($h > $maxh) {\n                    $h = $maxh;\n                    $w = abs($h * $info['w'] / $info['h']);\n                }\n            } elseif ($this->watermark_size == 'F') {\n                if ($this->ColActive) {\n                    $maxw = $this->w - ($this->DeflMargin + $this->DefrMargin);\n                } else {\n                    $maxw = $this->pgwidth;\n                }\n                $maxh = $this->h - ($this->tMargin + $this->bMargin);\n                $w = $maxw;\n                $h = abs($w * $info['h'] / $info['w']);\n                if ($h > $maxh) {\n                    $h = $maxh;\n                    $w = abs($h * $info['w'] / $info['h']);\n                }\n            } elseif ($this->watermark_size == 'P') { // Default P\n                $w = $maxw;\n                $h = abs($w * $info['h'] / $info['w']);\n                if ($h > $maxh) {\n                    $h = $maxh;\n                    $w = abs($h * $info['w'] / $info['h']);\n                }\n            }\n            // Automatically resize to maximum dimensions of page if too large\n            if ($w > $maxw) {\n                $w = $maxw;\n                $h = abs($w * $info['h'] / $info['w']);\n            }\n            if ($h > $maxh) {\n                $h = $maxh;\n                $w = abs($h * $info['w'] / $info['h']);\n            }\n            // Position\n            if (is_array($this->watermark_pos)) {\n                $x = $this->watermark_pos[0];\n                $y = $this->watermark_pos[1];\n            } elseif ($this->watermark_pos == 'F') { // centred on printable area\n                if ($this->ColActive) { // *COLUMNS*\n                    if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {\n                        $xadj = $this->DeflMargin - $this->DefrMargin;\n                    } // *COLUMNS*\n                    else {\n                        $xadj = 0;\n                    } // *COLUMNS*\n                    $x = ($this->DeflMargin - $xadj + ($this->w - ($this->DeflMargin + $this->DefrMargin)) / 2) - ($w / 2); // *COLUMNS*\n                } // *COLUMNS*\n                else {  // *COLUMNS*\n                    $x = ($this->lMargin + ($this->pgwidth) / 2) - ($w / 2);\n                } // *COLUMNS*\n                $y = ($this->tMargin + ($this->h - ($this->tMargin + $this->bMargin)) / 2) - ($h / 2);\n            } else { // default P - centred on whole page\n                $x = ($this->w / 2) - ($w / 2);\n                $y = ($this->h / 2) - ($h / 2);\n            }\n            /* -- IMAGES-WMF -- */\n            if ($info['type'] == 'wmf') {\n                $sx = $w * Mpdf::SCALE / $info['w'];\n                $sy = -$h * Mpdf::SCALE / $info['h'];\n                $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);\n            } else { \t\t\t/* -- END IMAGES-WMF -- */\n                if ($info['type'] == 'svg') {\n                    $sx = $w * Mpdf::SCALE / $info['w'];\n                    $sy = -$h * Mpdf::SCALE / $info['h'];\n                    $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);\n                } else {\n                    $outstring = sprintf(\"q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q\", $w * Mpdf::SCALE, $h * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $h)) * Mpdf::SCALE, $info['i']);\n                }\n            }\n\n            if ($this->watermarkImgBehind) {\n                $outstring = $this->watermarkImgAlpha . \"\\n\" . $outstring . \"\\n\" . $this->SetAlpha(1, 'Normal', true) . \"\\n\";\n                $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', \"\\n\" . $outstring . \"\\n\" . '\\\\1', $this->pages[$this->page]);\n            } else {\n                $this->writer->write($outstring);\n            }\n\n            return 0;\n        } // end of IF watermark\n        /* -- END WATERMARK -- */\n\n        if ($constrain) {\n            // Automatically resize to maximum dimensions of page if too large\n            if (isset($this->blk[$this->blklvl]['inner_width']) && $this->blk[$this->blklvl]['inner_width']) {\n                $maxw = $this->blk[$this->blklvl]['inner_width'];\n            } else {\n                $maxw = $this->pgwidth;\n            }\n            if ($w > $maxw) {\n                $w = $maxw;\n                $h = abs($w * $info['h'] / $info['w']);\n            }\n            if ($h > $this->h - ($this->tMargin + $this->bMargin + 1)) {  // see below - +10 to avoid drawing too close to border of page\n                $h = $this->h - ($this->tMargin + $this->bMargin + 1);\n                if ($this->fullImageHeight) {\n                    $h = $this->fullImageHeight;\n                }\n                $w = abs($h * $info['w'] / $info['h']);\n            }\n\n\n            // Avoid drawing out of the paper(exceeding width limits).\n            // if ( ($x + $w) > $this->fw ) {\n            if (($x + $w) > $this->w) {\n                $x = $this->lMargin;\n                $y += 5;\n            }\n\n            $changedpage = false;\n            $oldcolumn = $this->CurrCol;\n            // Avoid drawing out of the page.\n            if ($y + $h > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {\n                $this->AddPage($this->CurOrientation);\n                // Added to correct for OddEven Margins\n                $x = $x + $this->MarginCorrection;\n                $y = $this->tMargin; // mPDF 5.7.3\n                $changedpage = true;\n            }\n            /* -- COLUMNS -- */\n            // COLS\n            // COLUMN CHANGE\n            if ($this->CurrCol != $oldcolumn) {\n                $y = $this->y0;\n                $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n                $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n            }\n            /* -- END COLUMNS -- */\n        } // end of IF constrain\n\n        /* -- IMAGES-WMF -- */\n        if ($info['type'] == 'wmf') {\n            $sx = $w * Mpdf::SCALE / $info['w'];\n            $sy = -$h * Mpdf::SCALE / $info['h'];\n            $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);\n        } else { \t\t/* -- END IMAGES-WMF -- */\n            if ($info['type'] == 'svg') {\n                $sx = $w * Mpdf::SCALE / $info['w'];\n                $sy = -$h * Mpdf::SCALE / $info['h'];\n                $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * Mpdf::SCALE - $sx * $info['x'], (($this->h - $y) * Mpdf::SCALE) - $sy * $info['y'], $info['i']);\n            } else {\n                $outstring = sprintf(\"q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q\", $w * Mpdf::SCALE, $h * Mpdf::SCALE, $x * Mpdf::SCALE, ($this->h - ($y + $h)) * Mpdf::SCALE, $info['i']);\n            }\n        }\n\n        if ($paint) {\n            $this->writer->write($outstring);\n            if ($link) {\n                $this->Link($x, $y, $w, $h, $link);\n            }\n\n            // Avoid writing text on top of the image. // THIS WAS OUTSIDE THE if ($paint) bit!!!!!!!!!!!!!!!!\n            $this->y = $y + $h;\n        }\n\n        // Return width-height array\n        $sizesarray['WIDTH'] = $w;\n        $sizesarray['HEIGHT'] = $h;\n        $sizesarray['X'] = $x; // Position before painting image\n        $sizesarray['Y'] = $y; // Position before painting image\n        $sizesarray['OUTPUT'] = $outstring;\n\n        $sizesarray['IMAGE_ID'] = $info['i'];\n        $sizesarray['itype'] = $info['type'];\n        $sizesarray['set-dpi'] = (isset($info['set-dpi']) ? $info['set-dpi'] : 0);\n        return $sizesarray;\n    }\n\n    // =============================================================\n    // =============================================================\n    // =============================================================\n    // =============================================================\n    // =============================================================\n    /* -- HTML-CSS -- */\n\n    function _getObjAttr($t)\n    {\n        $c = explode(\"\\xbb\\xa4\\xac\", $t, 2);\n        $c = explode(\",\", $c[1], 2);\n        foreach ($c as $v) {\n            $v = explode(\"=\", $v, 2);\n            $sp[$v[0]] = $v[1];\n        }\n        return (unserialize($sp['objattr']));\n    }\n\n    function inlineObject($type, $x, $y, $objattr, $Lmargin, $widthUsed, $maxWidth, $lineHeight, $paint = false, $is_table = false)\n    {\n        if ($is_table) {\n            $k = $this->shrin_k;\n        } else {\n            $k = 1;\n        }\n\n        // NB $x is only used when paint=true\n        // Lmargin not used\n        $w = 0;\n        if (isset($objattr['width'])) {\n            $w = $objattr['width'] / $k;\n        }\n        $h = 0;\n        if (isset($objattr['height'])) {\n            $h = abs($objattr['height'] / $k);\n        }\n        $widthLeft = $maxWidth - $widthUsed;\n        $maxHeight = $this->h - ($this->tMargin + $this->bMargin + 10);\n        if ($this->fullImageHeight) {\n            $maxHeight = $this->fullImageHeight;\n        }\n        // For Images\n        if (isset($objattr['border_left'])) {\n            $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']) / $k;\n            $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']) / $k;\n\n            if ($type == 'image' || $type == 'barcode' || $type == 'textcircle') {\n                $extraWidth += ($objattr['padding_left'] + $objattr['padding_right']) / $k;\n                $extraHeight += ($objattr['padding_top'] + $objattr['padding_bottom']) / $k;\n            }\n        }\n\n        if (!isset($objattr['vertical-align'])) {\n            if ($objattr['type'] == 'select') {\n                $objattr['vertical-align'] = 'M';\n            } else {\n                $objattr['vertical-align'] = 'BS';\n            }\n        } // mPDF 6\n\n        if ($type == 'image' || (isset($objattr['subtype']) && $objattr['subtype'] == 'IMAGE')) {\n            if (isset($objattr['itype']) && ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg')) {\n                $file = $objattr['file'];\n                $info = $this->formobjects[$file];\n            } elseif (isset($objattr['file'])) {\n                $file = $objattr['file'];\n                $info = $this->images[$file];\n            }\n        }\n        if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {\n            $w = 0.00001;\n            $h = 0.00001;\n        }\n\n        // TEST whether need to skipline\n        if (!$paint) {\n            if ($type == 'hr') { // always force new line\n                if (($y + $h + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {\n                    return [-2, $w, $h];\n                } // New page + new line\n                else {\n                    return [1, $w, $h];\n                } // new line\n            } else {\n                // LIST MARKERS\t// mPDF 6  Lists\n                $displayheight = $h;\n                $displaywidth = $w;\n                if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker']) {\n                    $displayheight = 0;\n                    if ($objattr['listmarkerposition'] == 'outside') {\n                        $displaywidth = 0;\n                    }\n                }\n\n                if ($widthUsed > 0 && $displaywidth > $widthLeft && (!$is_table || $type != 'image')) {  // New line needed\n                    // mPDF 6  Lists\n                    if (($y + $displayheight + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter) {\n                        return [-2, $w, $h];\n                    } // New page + new line\n                    return [1, $w, $h]; // new line\n                } elseif ($widthUsed > 0 && $displaywidth > $widthLeft && $is_table) {  // New line needed in TABLE\n                    return [1, $w, $h]; // new line\n                } // Will fit on line but NEW PAGE REQUIRED\n                elseif (($y + $displayheight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {\n                    return [-1, $w, $h];\n                } // mPDF 6  Lists\n                else {\n                    return [0, $w, $h];\n                }\n            }\n        }\n\n        if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {\n            $w = 0.00001;\n            $h = 0.00001;\n            $objattr['BORDER-WIDTH'] = 0;\n            $objattr['BORDER-HEIGHT'] = 0;\n            $objattr['BORDER-X'] = $x;\n            $objattr['BORDER-Y'] = $y;\n            $objattr['INNER-WIDTH'] = 0;\n            $objattr['INNER-HEIGHT'] = 0;\n            $objattr['INNER-X'] = $x;\n            $objattr['INNER-Y'] = $y;\n        }\n\n        if ($type == 'image') {\n            // Automatically resize to width remaining\n            if ($w > ($widthLeft + 0.0001) && !$is_table) { // mPDF 5.7.4  0.0001 to allow for rounding errors when w==maxWidth\n                $w = $widthLeft;\n                $h = abs($w * $info['h'] / $info['w']);\n            }\n            $img_w = $w - $extraWidth;\n            $img_h = $h - $extraHeight;\n\n            $objattr['BORDER-WIDTH'] = $img_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);\n            $objattr['BORDER-HEIGHT'] = $img_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);\n            $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);\n            $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);\n            $objattr['INNER-WIDTH'] = $img_w;\n            $objattr['INNER-HEIGHT'] = $img_h;\n            $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);\n            $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);\n            $objattr['ID'] = $info['i'];\n        }\n\n        if ($type == 'input' && $objattr['subtype'] == 'IMAGE') {\n            $img_w = $w - $extraWidth;\n            $img_h = $h - $extraHeight;\n            $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);\n            $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);\n            $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);\n            $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);\n            $objattr['INNER-WIDTH'] = $img_w;\n            $objattr['INNER-HEIGHT'] = $img_h;\n            $objattr['INNER-X'] = $x + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);\n            $objattr['INNER-Y'] = $y + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);\n            $objattr['ID'] = $info['i'];\n        }\n\n        if ($type == 'barcode' || $type == 'textcircle') {\n            $b_w = $w - $extraWidth;\n            $b_h = $h - $extraHeight;\n            $objattr['BORDER-WIDTH'] = $b_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);\n            $objattr['BORDER-HEIGHT'] = $b_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);\n            $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);\n            $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);\n            $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);\n            $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);\n            $objattr['INNER-WIDTH'] = $b_w;\n            $objattr['INNER-HEIGHT'] = $b_h;\n        }\n\n\n        if ($type == 'textarea') {\n            // Automatically resize to width remaining\n            if ($w > $widthLeft && !$is_table) {\n                $w = $widthLeft;\n            }\n            // This used to resize height to maximum remaining on page ? why. Causes problems when in table and causing a new column\n            // if (($y + $h > $this->PageBreakTrigger) && !$this->InFooter) {\n            // \t$h=$this->h - $y - $this->bMargin;\n            // }\n        }\n\n        if ($type == 'hr') {\n            if ($is_table) {\n                $objattr['INNER-WIDTH'] = $maxWidth * $objattr['W-PERCENT'] / 100;\n                $objattr['width'] = $objattr['INNER-WIDTH'];\n                $w = $maxWidth;\n            } else {\n                if ($w > $maxWidth) {\n                    $w = $maxWidth;\n                }\n                $objattr['INNER-WIDTH'] = $w;\n                $w = $maxWidth;\n            }\n        }\n\n\n\n        if (($type == 'select') || ($type == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD'))) {\n            // Automatically resize to width remaining\n            if ($w > $widthLeft && !$is_table) {\n                $w = $widthLeft;\n            }\n        }\n\n        if ($type == 'textarea' || $type == 'select' || $type == 'input') {\n            if (isset($objattr['fontsize'])) {\n                $objattr['fontsize'] /= $k;\n            }\n            if (isset($objattr['linewidth'])) {\n                $objattr['linewidth'] /= $k;\n            }\n        }\n\n        if (!isset($objattr['BORDER-Y'])) {\n            $objattr['BORDER-Y'] = 0;\n        }\n        if (!isset($objattr['BORDER-X'])) {\n            $objattr['BORDER-X'] = 0;\n        }\n        if (!isset($objattr['INNER-Y'])) {\n            $objattr['INNER-Y'] = 0;\n        }\n        if (!isset($objattr['INNER-X'])) {\n            $objattr['INNER-X'] = 0;\n        }\n\n        // Return width-height array\n        $objattr['OUTER-WIDTH'] = $w;\n        $objattr['OUTER-HEIGHT'] = $h;\n        $objattr['OUTER-X'] = $x;\n        $objattr['OUTER-Y'] = $y;\n        return $objattr;\n    }\n\n    /* -- END HTML-CSS -- */\n\n    // =============================================================\n    // =============================================================\n    // =============================================================\n    // =============================================================\n    // =============================================================\n\n    function SetLineJoin($mode = 0)\n    {\n        $s = sprintf('%d j', $mode);\n        if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineJoin']) && $this->pageoutput[$this->page]['LineJoin'] != $s) || !isset($this->pageoutput[$this->page]['LineJoin']))) {\n            $this->writer->write($s);\n        }\n        $this->pageoutput[$this->page]['LineJoin'] = $s;\n    }\n\n    function SetLineCap($mode = 2)\n    {\n        $s = sprintf('%d J', $mode);\n        if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineCap']) && $this->pageoutput[$this->page]['LineCap'] != $s) || !isset($this->pageoutput[$this->page]['LineCap']))) {\n            $this->writer->write($s);\n        }\n        $this->pageoutput[$this->page]['LineCap'] = $s;\n    }\n\n    function SetDash($black = false, $white = false)\n    {\n        if ($black and $white) {\n            $s = sprintf('[%.3F %.3F] 0 d', $black * Mpdf::SCALE, $white * Mpdf::SCALE);\n        } else {\n            $s = '[] 0 d';\n        }\n        if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Dash']) && $this->pageoutput[$this->page]['Dash'] != $s) || !isset($this->pageoutput[$this->page]['Dash']))) {\n            $this->writer->write($s);\n        }\n        $this->pageoutput[$this->page]['Dash'] = $s;\n    }\n\n    function SetDisplayPreferences($preferences)\n    {\n        // String containing any or none of /HideMenubar/HideToolbar/HideWindowUI/DisplayDocTitle/CenterWindow/FitWindow\n        $this->DisplayPreferences .= $preferences;\n    }\n\n    function Ln($h = '', $collapsible = 0)\n    {\n        // Added collapsible to allow collapsible top-margin on new page\n        // Line feed; default value is last cell height\n        $this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'];\n        if ($collapsible && ($this->y == $this->tMargin) && (!$this->ColActive)) {\n            $h = 0;\n        }\n        if (is_string($h)) {\n            $this->y+=$this->lasth;\n        } else {\n            $this->y+=$h;\n        }\n    }\n\n    /* -- HTML-CSS -- */\n\n    function DivLn($h, $level = -3, $move_y = true, $collapsible = false, $state = 0)\n    {\n        // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom\n        // Used in Columns and keep-with-table i.e. \"kwt\"\n        // writes background block by block so it can be repositioned\n        // and also used in writingFlowingBlock at top and bottom of blocks to move y (not to draw/paint anything)\n        // adds lines (y) where DIV bgcolors are filled in\n        // this->x is returned as it was\n        // allows .00001 as nominal height used for bookmarks/annotations etc.\n        if ($collapsible && (sprintf(\"%0.4f\", $this->y) == sprintf(\"%0.4f\", $this->tMargin)) && (!$this->ColActive)) {\n            return;\n        }\n\n        // mPDF 6 Columns\n        //   if ($collapsible && (sprintf(\"%0.4f\", $this->y)==sprintf(\"%0.4f\", $this->y0)) && ($this->ColActive) && $this->CurrCol == 0) { return; }\t// *COLUMNS*\n        if ($collapsible && (sprintf(\"%0.4f\", $this->y) == sprintf(\"%0.4f\", $this->y0)) && ($this->ColActive)) {\n            return;\n        } // *COLUMNS*\n        // Still use this method if columns or keep-with-table, as it allows repositioning later\n        // otherwise, now uses PaintDivBB()\n        if (!$this->ColActive && !$this->kwt) {\n            if ($move_y && !$this->ColActive) {\n                $this->y += $h;\n            }\n            return;\n        }\n\n        if ($level == -3) {\n            $level = $this->blklvl;\n        }\n        $firstblockfill = $this->GetFirstBlockFill();\n        if ($firstblockfill && $this->blklvl > 0 && $this->blklvl >= $firstblockfill) {\n            $last_x = 0;\n            $last_w = 0;\n            $last_fc = $this->FillColor;\n            $bak_x = $this->x;\n            $bak_h = $this->divheight;\n            $this->divheight = 0; // Temporarily turn off divheight - as Cell() uses it to check for PageBreak\n            for ($blvl = $firstblockfill; $blvl <= $level; $blvl++) {\n                $this->x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];\n                // mPDF 6\n                if ($this->blk[$blvl]['bgcolor']) {\n                    $this->SetFColor($this->blk[$blvl]['bgcolorarray']);\n                }\n                if ($last_x != ($this->lMargin + $this->blk[$blvl]['outer_left_margin']) || ($last_w != $this->blk[$blvl]['width']) || $last_fc != $this->FillColor || (isset($this->blk[$blvl]['border_top']['s']) && $this->blk[$blvl]['border_top']['s']) || (isset($this->blk[$blvl]['border_bottom']['s']) && $this->blk[$blvl]['border_bottom']['s']) || (isset($this->blk[$blvl]['border_left']['s']) && $this->blk[$blvl]['border_left']['s']) || (isset($this->blk[$blvl]['border_right']['s']) && $this->blk[$blvl]['border_right']['s'])) {\n                    $x = $this->x;\n                    $this->Cell(($this->blk[$blvl]['width']), $h, '', '', 0, '', 1);\n                    $this->x = $x;\n                    if (!$this->keep_block_together && !$this->writingHTMLheader && !$this->writingHTMLfooter) {\n                        // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom\n                        if ($blvl == $this->blklvl) {\n                            $this->PaintDivLnBorder($state, $blvl, $h);\n                        } else {\n                            $this->PaintDivLnBorder(0, $blvl, $h);\n                        }\n                    }\n                }\n                $last_x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];\n                $last_w = $this->blk[$blvl]['width'];\n                $last_fc = $this->FillColor;\n            }\n            // Reset current block fill\n            if (isset($this->blk[$this->blklvl]['bgcolorarray'])) {\n                $bcor = $this->blk[$this->blklvl]['bgcolorarray'];\n                $this->SetFColor($bcor);\n            }\n            $this->x = $bak_x;\n            $this->divheight = $bak_h;\n        }\n        if ($move_y) {\n            $this->y += $h;\n        }\n    }\n\n    /* -- END HTML-CSS -- */\n\n    function SetX($x)\n    {\n        // Set x position\n        if ($x >= 0) {\n            $this->x = $x;\n        } else {\n            $this->x = $this->w + $x;\n        }\n    }\n\n    function SetY($y)\n    {\n        // Set y position and reset x\n        $this->x = $this->lMargin;\n        if ($y >= 0) {\n            $this->y = $y;\n        } else {\n            $this->y = $this->h + $y;\n        }\n    }\n\n    function SetXY($x, $y)\n    {\n        // Set x and y positions\n        $this->SetY($y);\n        $this->SetX($x);\n    }\n\n    function Output($name = '', $dest = '')\n    {\n        $this->logger->debug(sprintf('PDF generated in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);\n\n        // Finish document if necessary\n        if ($this->state < 3) {\n            $this->Close();\n        }\n\n        if ($this->debug && error_get_last()) {\n            $e = error_get_last();\n            if (($e['type'] < 2048 && $e['type'] != 8) || (intval($e['type']) & intval(ini_get(\"error_reporting\")))) {\n                throw new \\Mpdf\\MpdfException(\n                    sprintf('Error detected. PDF file generation aborted: %s', $e['message']),\n                    $e['type'],\n                    1,\n                    $e['file'],\n                    $e['line']\n                );\n            }\n        }\n\n        if (($this->PDFA || $this->PDFX) && $this->encrypted) {\n            throw new \\Mpdf\\MpdfException('PDF/A1-b or PDF/X1-a does not permit encryption of documents.');\n        }\n\n        if (count($this->PDFAXwarnings) && (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto))) {\n            if ($this->PDFA) {\n                $standard = 'PDFA/1-b';\n                $option = '$mpdf->PDFAauto';\n            } else {\n                $standard = 'PDFX/1-a ';\n                $option = '$mpdf->PDFXauto';\n            }\n\n            $this->logger->warning(sprintf('PDF could not be generated as it stands as a %s compliant file.', $standard), ['context' => LogContext::PDFA_PDFX]);\n            $this->logger->warning(sprintf('These issues can be automatically fixed by mPDF using %s = true;', $option), ['context' => LogContext::PDFA_PDFX]);\n            $this->logger->warning(sprintf('Action that mPDF will take to automatically force %s compliance are shown further in the log.', $standard), ['context' => LogContext::PDFA_PDFX]);\n\n            $this->PDFAXwarnings = array_unique($this->PDFAXwarnings);\n            foreach ($this->PDFAXwarnings as $w) {\n                $this->logger->warning($w, ['context' => LogContext::PDFA_PDFX]);\n            }\n\n            throw new \\Mpdf\\MpdfException('PDFA/PDFX warnings generated. See log for further details');\n        }\n\n        $this->logger->debug(sprintf('Compiled in %.6F seconds', microtime(true) - $this->time0), ['context' => LogContext::STATISTICS]);\n        $this->logger->debug(sprintf('Peak Memory usage %s MB', number_format(memory_get_peak_usage(true) / (1024 * 1024), 2)), ['context' => LogContext::STATISTICS]);\n        $this->logger->debug(sprintf('PDF file size %s kB', number_format(strlen($this->buffer) / 1024)), ['context' => LogContext::STATISTICS]);\n        $this->logger->debug(sprintf('%d fonts used', count($this->fonts)), ['context' => LogContext::STATISTICS]);\n\n        if (is_bool($dest)) {\n            $dest = $dest ? Destination::DOWNLOAD : Destination::FILE;\n        }\n\n        $dest = strtoupper($dest);\n        if (empty($dest)) {\n            if (empty($name)) {\n                $name = 'mpdf.pdf';\n                $dest = Destination::INLINE;\n            } else {\n                $dest = Destination::FILE;\n            }\n        }\n\n        switch ($dest) {\n\n            case Destination::INLINE:\n\n                if (headers_sent($filename, $line)) {\n                    throw new \\Mpdf\\MpdfException(\n                        sprintf('Data has already been sent to output (%s at line %s), unable to output PDF file', $filename, $line)\n                    );\n                }\n\n                if ($this->debug && !$this->allow_output_buffering && ob_get_contents()) {\n                    throw new \\Mpdf\\MpdfException('Output has already been sent from the script - PDF file generation aborted.');\n                }\n\n                // We send to a browser\n                if (PHP_SAPI !== 'cli') {\n                    header('Content-Type: application/pdf');\n\n                    if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {\n                        // don't use length if server using compression\n                        header('Content-Length: ' . strlen($this->buffer));\n                    }\n\n                    header('Content-disposition: inline; filename=\"' . $name . '\"');\n                    header('Cache-Control: public, must-revalidate, max-age=0');\n                    header('Pragma: public');\n                    header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : ''));\n                    header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');\n                    header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');\n                }\n\n                echo $this->buffer;\n\n                break;\n\n            case Destination::DOWNLOAD:\n\n                if (headers_sent()) {\n                    throw new \\Mpdf\\MpdfException('Data has already been sent to output, unable to output PDF file');\n                }\n\n                header('Content-Description: File Transfer');\n                header('Content-Transfer-Encoding: binary');\n                header('Cache-Control: public, must-revalidate, max-age=0');\n                header('Pragma: public');\n                header('X-Generator: mPDF' . ($this->exposeVersion ? (' ' . static::VERSION) : ''));\n                header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');\n                header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');\n                header('Content-Type: application/pdf');\n\n                if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) || empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {\n                    // don't use length if server using compression\n                    header('Content-Length: ' . strlen($this->buffer));\n                }\n\n                header('Content-Disposition: attachment; filename=\"' . $name . '\"');\n\n                echo $this->buffer;\n\n                break;\n\n            case Destination::FILE:\n                $f = fopen($name, 'wb');\n\n                if (!$f) {\n                    throw new \\Mpdf\\MpdfException(sprintf('Unable to create output file %s', $name));\n                }\n\n                fwrite($f, $this->buffer, strlen($this->buffer));\n                fclose($f);\n\n                break;\n\n            case Destination::STRING_RETURN:\n                $this->cache->clearOld();\n                return $this->buffer;\n\n            default:\n                throw new \\Mpdf\\MpdfException(sprintf('Incorrect output destination %s', $dest));\n        }\n\n        $this->cache->clearOld();\n    }\n\n    // *****************************************************************************\n    //                                                                             *\n    //                             Protected methods                               *\n    //                                                                             *\n    // *****************************************************************************\n    function _dochecks()\n    {\n        // Check for locale-related bug\n        if (1.1 == 1) {\n            throw new \\Mpdf\\MpdfException('Do not alter the locale before including mPDF');\n        }\n\n        // Check for decimal separator\n        if (sprintf('%.1f', 1.0) != '1.0') {\n            setlocale(LC_NUMERIC, 'C');\n        }\n\n        if (ini_get('mbstring.func_overload')) {\n            throw new \\Mpdf\\MpdfException('Mpdf cannot function properly with mbstring.func_overload enabled');\n        }\n\n        if (!function_exists('mb_substr')) {\n            throw new \\Mpdf\\MpdfException('mbstring extension must be loaded in order to run mPDF');\n        }\n    }\n\n    function _puthtmlheaders()\n    {\n        $this->state = 2;\n        $nb = $this->page;\n        for ($n = 1; $n <= $nb; $n++) {\n            if ($this->mirrorMargins && $n % 2 == 0) {\n                $OE = 'E';\n            } // EVEN\n            else {\n                $OE = 'O';\n            }\n            $this->page = $n;\n            $pn = $this->docPageNum($n);\n            if ($pn) {\n                $pnstr = $this->pagenumPrefix . $pn . $this->pagenumSuffix;\n            } else {\n                $pnstr = '';\n            }\n\n            $pnt = $this->docPageNumTotal($n);\n\n            if ($pnt) {\n                $pntstr = $this->nbpgPrefix . $pnt . $this->nbpgSuffix;\n            } else {\n                $pntstr = '';\n            }\n\n            if (isset($this->saveHTMLHeader[$n][$OE])) {\n                $html = isset($this->saveHTMLHeader[$n][$OE]['html']) ? $this->saveHTMLHeader[$n][$OE]['html'] : '';\n                $this->lMargin = $this->saveHTMLHeader[$n][$OE]['ml'];\n                $this->rMargin = $this->saveHTMLHeader[$n][$OE]['mr'];\n                $this->tMargin = $this->saveHTMLHeader[$n][$OE]['mh'];\n                $this->bMargin = $this->saveHTMLHeader[$n][$OE]['mf'];\n                $this->margin_header = $this->saveHTMLHeader[$n][$OE]['mh'];\n                $this->margin_footer = $this->saveHTMLHeader[$n][$OE]['mf'];\n                $this->w = $this->saveHTMLHeader[$n][$OE]['pw'];\n                $this->h = $this->saveHTMLHeader[$n][$OE]['ph'];\n                $rotate = (isset($this->saveHTMLHeader[$n][$OE]['rotate']) ? $this->saveHTMLHeader[$n][$OE]['rotate'] : null);\n                $this->Reset();\n                $this->pageoutput[$n] = [];\n                $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n                $this->x = $this->lMargin;\n                $this->y = $this->margin_header;\n                $html = str_replace('{PAGENO}', $pnstr, $html);\n                $html = str_replace($this->aliasNbPgGp, $pntstr, $html); // {nbpg}\n                $html = str_replace($this->aliasNbPg, $nb, $html); // {nb}\n                $html = preg_replace_callback('/\\{DATE\\s+(.*?)\\}/', [$this, 'date_callback'], $html); // mPDF 5.7\n\n                $this->HTMLheaderPageLinks = [];\n                $this->HTMLheaderPageAnnots = [];\n                $this->HTMLheaderPageForms = [];\n                $this->pageBackgrounds = [];\n\n                $this->writingHTMLheader = true;\n                $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);\n                $this->writingHTMLheader = false;\n                $this->Reset();\n                $this->pageoutput[$n] = [];\n\n                $s = $this->PrintPageBackgrounds();\n                $this->headerbuffer = $s . $this->headerbuffer;\n                $os = '';\n                if ($rotate) {\n                    $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));\n                    // To rotate the other way i.e. Header to left of page:\n                    // $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));\n                }\n                $os .= $this->headerbuffer;\n                if ($rotate) {\n                    $os .= ' Q' . \"\\n\";\n                }\n\n                // Writes over the page background but behind any other output on page\n                $os = preg_replace(['/\\\\\\\\/', '/\\$/'], ['\\\\\\\\\\\\\\\\', '\\\\\\\\$'], $os);\n\n                $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', \"\\n\" . $os . \"\\n\" . '\\\\1', $this->pages[$n]);\n\n                $lks = $this->HTMLheaderPageLinks;\n                foreach ($lks as $lk) {\n                    if ($rotate) {\n                        $lw = $lk[2];\n                        $lh = $lk[3];\n                        $lk[2] = $lh;\n                        $lk[3] = $lw; // swap width and height\n                        $ax = $lk[0] / Mpdf::SCALE;\n                        $ay = $lk[1] / Mpdf::SCALE;\n                        $bx = $ay - ($lh / Mpdf::SCALE);\n                        $by = $this->w - $ax;\n                        $lk[0] = $bx * Mpdf::SCALE;\n                        $lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;\n                    }\n                    $this->PageLinks[$n][] = $lk;\n                }\n                /* -- FORMS -- */\n                foreach ($this->HTMLheaderPageForms as $f) {\n                    $this->form->forms[$f['n']] = $f;\n                }\n                /* -- END FORMS -- */\n            }\n\n            if (isset($this->saveHTMLFooter[$n][$OE])) {\n\n                $html = $this->saveHTMLFooter[$this->page][$OE]['html'];\n\n                $this->lMargin = $this->saveHTMLFooter[$n][$OE]['ml'];\n                $this->rMargin = $this->saveHTMLFooter[$n][$OE]['mr'];\n                $this->tMargin = $this->saveHTMLFooter[$n][$OE]['mh'];\n                $this->bMargin = $this->saveHTMLFooter[$n][$OE]['mf'];\n                $this->margin_header = $this->saveHTMLFooter[$n][$OE]['mh'];\n                $this->margin_footer = $this->saveHTMLFooter[$n][$OE]['mf'];\n                $this->w = $this->saveHTMLFooter[$n][$OE]['pw'];\n                $this->h = $this->saveHTMLFooter[$n][$OE]['ph'];\n                $rotate = (isset($this->saveHTMLFooter[$n][$OE]['rotate']) ? $this->saveHTMLFooter[$n][$OE]['rotate'] : null);\n                $this->Reset();\n                $this->pageoutput[$n] = [];\n                $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n                $this->x = $this->lMargin;\n                $top_y = $this->y = $this->h - $this->margin_footer;\n\n                // if bottom-margin==0, corrects to avoid division by zero\n                if ($this->y == $this->h) {\n                    $top_y = $this->y = ($this->h + 0.01);\n                }\n\n                $html = str_replace('{PAGENO}', $pnstr, $html);\n                $html = str_replace($this->aliasNbPgGp, $pntstr, $html); // {nbpg}\n                $html = str_replace($this->aliasNbPg, $nb, $html); // {nb}\n                $html = preg_replace_callback('/\\{DATE\\s+(.*?)\\}/', [$this, 'date_callback'], $html); // mPDF 5.7\n\n\n                $this->HTMLheaderPageLinks = [];\n                $this->HTMLheaderPageAnnots = [];\n                $this->HTMLheaderPageForms = [];\n                $this->pageBackgrounds = [];\n\n                $this->writingHTMLfooter = true;\n                $this->InFooter = true;\n                $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);\n                $this->InFooter = false;\n                $this->Reset();\n                $this->pageoutput[$n] = [];\n\n                $fheight = $this->y - $top_y;\n                $adj = -$fheight;\n\n                $s = $this->PrintPageBackgrounds(-$adj);\n                $this->headerbuffer = $s . $this->headerbuffer;\n                $this->writingHTMLfooter = false; // mPDF 5.7.3  (moved after PrintPageBackgrounds so can adjust position of images in footer)\n\n                $os = '';\n                $os .= $this->StartTransform(true) . \"\\n\";\n\n                if ($rotate) {\n                    $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * Mpdf::SCALE));\n                    // To rotate the other way i.e. Header to left of page:\n                    // $os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*Mpdf::SCALE), (($this->rMargin - $this->lMargin )*Mpdf::SCALE));\n                }\n\n                $os .= $this->transformTranslate(0, $adj, true) . \"\\n\";\n                $os .= $this->headerbuffer;\n\n                if ($rotate) {\n                    $os .= ' Q' . \"\\n\";\n                }\n\n                $os .= $this->StopTransform(true) . \"\\n\";\n\n                // Writes over the page background but behind any other output on page\n                $os = preg_replace(['/\\\\\\\\/', '/\\$/'], ['\\\\\\\\\\\\\\\\', '\\\\\\\\$'], $os);\n\n                $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', \"\\n\" . $os . \"\\n\" . '\\\\1', $this->pages[$n]);\n\n                $lks = $this->HTMLheaderPageLinks;\n\n                foreach ($lks as $lk) {\n\n                    $lk[1] -= $adj * Mpdf::SCALE;\n\n                    if ($rotate) {\n                        $lw = $lk[2];\n                        $lh = $lk[3];\n                        $lk[2] = $lh;\n                        $lk[3] = $lw; // swap width and height\n\n                        $ax = $lk[0] / Mpdf::SCALE;\n                        $ay = $lk[1] / Mpdf::SCALE;\n                        $bx = $ay - ($lh / Mpdf::SCALE);\n                        $by = $this->w - $ax;\n                        $lk[0] = $bx * Mpdf::SCALE;\n                        $lk[1] = ($this->h - $by) * Mpdf::SCALE - $lw;\n                    }\n\n                    $this->PageLinks[$n][] = $lk;\n                }\n\n                /* -- FORMS -- */\n                foreach ($this->HTMLheaderPageForms as $f) {\n                    $f['y'] += $adj;\n                    $this->form->forms[$f['n']] = $f;\n                }\n                /* -- END FORMS -- */\n            }\n        }\n\n        $this->page = $nb;\n        $this->state = 1;\n    }\n\n    /* -- ANNOTATIONS -- */\n    function Annotation($text, $x = 0, $y = 0, $icon = 'Note', $author = '', $subject = '', $opacity = 0, $colarray = false, $popup = '', $file = '')\n    {\n        if (is_array($colarray) && count($colarray) == 3) {\n            $colarray = $this->colorConverter->convert('rgb(' . $colarray[0] . ',' . $colarray[1] . ',' . $colarray[2] . ')', $this->PDFAXwarnings);\n        }\n        if ($colarray === false) {\n            $colarray = $this->colorConverter->convert('yellow', $this->PDFAXwarnings);\n        }\n        if ($x == 0) {\n            $x = $this->x;\n        }\n        if ($y == 0) {\n            $y = $this->y;\n        }\n        $page = $this->page;\n        if ($page < 1) { // Document has not been started - assume it's for first page\n            $page = 1;\n            if ($x == 0) {\n                $x = $this->lMargin;\n            }\n            if ($y == 0) {\n                $y = $this->tMargin;\n            }\n        }\n\n        if ($this->PDFA || $this->PDFX) {\n            if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {\n                $this->PDFAXwarnings[] = \"Annotation markers cannot be semi-transparent in PDFA1-b or PDFX/1-a, so they may make underlying text unreadable. (Annotation markers moved to right margin)\";\n            }\n            $x = ($this->w) - $this->rMargin * 0.66;\n        }\n        if (!$this->annotMargin) {\n            $y -= $this->FontSize / 2;\n        }\n\n        if (!$opacity && $this->annotMargin) {\n            $opacity = 1;\n        } elseif (!$opacity) {\n            $opacity = $this->annotOpacity;\n        }\n\n        $an = ['txt' => $text, 'x' => $x, 'y' => $y, 'opt' => ['Icon' => $icon, 'T' => $author, 'Subj' => $subject, 'C' => $colarray, 'CA' => $opacity, 'popup' => $popup, 'file' => $file]];\n\n        if ($this->keep_block_together) { // don't write yet\n            return;\n        } elseif ($this->table_rotate) {\n            $this->tbrot_Annots[$this->page][] = $an;\n            return;\n        } elseif ($this->kwt) {\n            $this->kwt_Annots[$this->page][] = $an;\n            return;\n        }\n\n        if ($this->writingHTMLheader || $this->writingHTMLfooter) {\n            $this->HTMLheaderPageAnnots[] = $an;\n            return;\n        }\n\n        // Put an Annotation on the page\n        $this->PageAnnots[$page][] = $an;\n\n        /* -- COLUMNS -- */\n        // Save cross-reference to Column buffer\n        $ref = isset($this->PageAnnots[$this->page]) ? (count($this->PageAnnots[$this->page]) - 1) : -1;\n        $this->columnAnnots[$this->CurrCol][intval($this->x)][intval($this->y)] = $ref;\n        /* -- END COLUMNS -- */\n    }\n\n    /* -- END ANNOTATIONS -- */\n\n    function _enddoc()\n    {\n        // @log Writing Headers & Footers\n\n        $this->_puthtmlheaders();\n\n        // @log Writing Pages\n\n        // Remove references to unused fonts (usually default font)\n        foreach ($this->fonts as $fk => $font) {\n            if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {\n                if ($font['sip'] || $font['smp']) {\n                    foreach ($font['subsetfontids'] as $k => $fid) {\n                        foreach ($this->pages as $pn => $page) {\n                            $this->pages[$pn] = preg_replace('/\\s\\/F' . $fid . ' \\d[\\d.]* Tf\\s/is', ' ', $this->pages[$pn]);\n                        }\n                    }\n                } else {\n                    foreach ($this->pages as $pn => $page) {\n                        $this->pages[$pn] = preg_replace('/\\s\\/F' . $font['i'] . ' \\d[\\d.]* Tf\\s/is', ' ', $this->pages[$pn]);\n                    }\n                }\n            }\n        }\n\n        if (count($this->layers)) {\n            foreach ($this->pages as $pn => $page) {\n                preg_match_all('/\\/OCZ-index \\/ZI(\\d+) BDC(.*?)(EMCZ)-index/is', $this->pages[$pn], $m1);\n                preg_match_all('/\\/OCBZ-index \\/ZI(\\d+) BDC(.*?)(EMCBZ)-index/is', $this->pages[$pn], $m2);\n                preg_match_all('/\\/OCGZ-index \\/ZI(\\d+) BDC(.*?)(EMCGZ)-index/is', $this->pages[$pn], $m3);\n                $m = [];\n                for ($i = 0; $i < 4; $i++) {\n                    $m[$i] = array_merge($m1[$i], $m2[$i], $m3[$i]);\n                }\n                if (count($m[0])) {\n                    $sortarr = [];\n                    for ($i = 0; $i < count($m[0]); $i++) {\n                        $key = $m[1][$i] * 2;\n                        if ($m[3][$i] == 'EMCZ') {\n                            $key +=2; // background first then gradient then normal\n                        } elseif ($m[3][$i] == 'EMCGZ') {\n                            $key +=1;\n                        }\n                        $sortarr[$i] = $key;\n                    }\n                    asort($sortarr);\n                    foreach ($sortarr as $i => $k) {\n                        $this->pages[$pn] = str_replace($m[0][$i], '', $this->pages[$pn]);\n                        $this->pages[$pn] .= \"\\n\" . $m[0][$i] . \"\\n\";\n                    }\n                    $this->pages[$pn] = preg_replace('/\\/OC[BG]{0,1}Z-index \\/ZI(\\d+) BDC/is', '/OC /ZI\\\\1 BDC ', $this->pages[$pn]);\n                    $this->pages[$pn] = preg_replace('/EMC[BG]{0,1}Z-index/is', 'EMC', $this->pages[$pn]);\n                }\n            }\n        }\n\n        $this->pageWriter->writePages();\n\n        // @log Writing document resources\n\n        $this->resourceWriter->writeResources();\n\n        // Info\n        $this->writer->object();\n        $this->InfoRoot = $this->n;\n        $this->writer->write('<<');\n\n        // @log Writing document info\n        $this->metadataWriter->writeInfo();\n\n        $this->writer->write('>>');\n        $this->writer->write('endobj');\n\n        // METADATA\n        if ($this->PDFA || $this->PDFX) {\n            $this->metadataWriter->writeMetadata();\n        }\n\n        // OUTPUTINTENT\n        if ($this->PDFA || $this->PDFX || $this->ICCProfile) {\n            $this->metadataWriter->writeOutputIntent();\n        }\n\n        // Associated files\n        if ($this->associatedFiles) {\n            $this->metadataWriter->writeAssociatedFiles();\n        }\n\n        // Catalog\n        $this->writer->object();\n        $this->writer->write('<<');\n\n        // @log Writing document catalog\n\n        $this->metadataWriter->writeCatalog();\n\n        $this->writer->write('>>');\n        $this->writer->write('endobj');\n\n        // Cross-ref\n        $o = strlen($this->buffer);\n        $this->writer->write('xref');\n        $this->writer->write('0 ' . ($this->n + 1));\n        $this->writer->write('0000000000 65535 f ');\n\n        for ($i = 1; $i <= $this->n; $i++) {\n            $this->writer->write(sprintf('%010d 00000 n ', $this->offsets[$i]));\n        }\n\n        // Trailer\n        $this->writer->write('trailer');\n        $this->writer->write('<<');\n\n        $this->metadataWriter->writeTrailer();\n\n        $this->writer->write('>>');\n        $this->writer->write('startxref');\n        $this->writer->write($o);\n\n        $this->buffer .= '%%EOF';\n        $this->state = 3;\n    }\n\n    function _beginpage(\n        $orientation,\n        $mgl = '',\n        $mgr = '',\n        $mgt = '',\n        $mgb = '',\n        $mgh = '',\n        $mgf = '',\n        $ohname = '',\n        $ehname = '',\n        $ofname = '',\n        $efname = '',\n        $ohvalue = 0,\n        $ehvalue = 0,\n        $ofvalue = 0,\n        $efvalue = 0,\n        $pagesel = '',\n        $newformat = ''\n    ) {\n        if (!($pagesel && $this->page == 1 && (sprintf(\"%0.4f\", $this->y) == sprintf(\"%0.4f\", $this->tMargin)))) {\n            $this->page++;\n            $this->pages[$this->page] = '';\n        }\n        $this->state = 2;\n        $resetHTMLHeadersrequired = false;\n\n        if ($newformat) {\n            $this->_setPageSize($newformat, $orientation);\n        }\n\n        /* -- CSS-PAGE -- */\n        // Paged media (page-box)\n        if ($pagesel || (isset($this->page_box['using']) && $this->page_box['using'])) {\n\n            if ($pagesel || $this->page == 1) {\n                $first = true;\n            } else {\n                $first = false;\n            }\n\n            if ($this->mirrorMargins && ($this->page % 2 == 0)) {\n                $oddEven = 'E';\n            } else {\n                $oddEven = 'O';\n            }\n\n            if ($pagesel) {\n                $psel = $pagesel;\n            } elseif ($this->page_box['current']) {\n                $psel = $this->page_box['current'];\n            } else {\n                $psel = '';\n            }\n\n            [$orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat] = $this->SetPagedMediaCSS($psel, $first, $oddEven);\n\n            if ($this->mirrorMargins && ($this->page % 2 == 0)) {\n\n                if ($hname) {\n                    $ehvalue = 1;\n                    $ehname = $hname;\n                } else {\n                    $ehvalue = -1;\n                }\n\n                if ($fname) {\n                    $efvalue = 1;\n                    $efname = $fname;\n                } else {\n                    $efvalue = -1;\n                }\n\n            } else {\n\n                if ($hname) {\n                    $ohvalue = 1;\n                    $ohname = $hname;\n                } else {\n                    $ohvalue = -1;\n                }\n\n                if ($fname) {\n                    $ofvalue = 1;\n                    $ofname = $fname;\n                } else {\n                    $ofvalue = -1;\n                }\n            }\n\n            if ($resetpagenum || $pagenumstyle || $suppress) {\n                $this->PageNumSubstitutions[] = ['from' => ($this->page), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];\n            }\n\n            // PAGED MEDIA - CROP / CROSS MARKS from @PAGE\n            $this->show_marks = $marks;\n\n            // Background color\n            if (isset($bg['BACKGROUND-COLOR'])) {\n                $cor = $this->colorConverter->convert($bg['BACKGROUND-COLOR'], $this->PDFAXwarnings);\n                if ($cor) {\n                    $this->bodyBackgroundColor = $cor;\n                }\n            } else {\n                $this->bodyBackgroundColor = false;\n            }\n\n            /* -- BACKGROUNDS -- */\n            if (isset($bg['BACKGROUND-GRADIENT'])) {\n                $this->bodyBackgroundGradient = $bg['BACKGROUND-GRADIENT'];\n            } else {\n                $this->bodyBackgroundGradient = false;\n            }\n\n            // Tiling Patterns\n            if (isset($bg['BACKGROUND-IMAGE']) && $bg['BACKGROUND-IMAGE']) {\n                $ret = $this->SetBackground($bg, $this->pgwidth);\n                if ($ret) {\n                    $this->bodyBackgroundImage = $ret;\n                }\n            } else {\n                $this->bodyBackgroundImage = false;\n            }\n            /* -- END BACKGROUNDS -- */\n\n            $this->page_box['current'] = $psel;\n            $this->page_box['using'] = true;\n        }\n        /* -- END CSS-PAGE -- */\n\n        // Page orientation\n        if (!$orientation) {\n            $orientation = $this->DefOrientation;\n        } else {\n            $orientation = strtoupper(substr($orientation, 0, 1));\n            if ($orientation != $this->DefOrientation) {\n                $this->OrientationChanges[$this->page] = true;\n            }\n        }\n\n        if ($orientation != $this->CurOrientation || $newformat) {\n\n            // Change orientation\n            if ($orientation == 'P') {\n                $this->wPt = $this->fwPt;\n                $this->hPt = $this->fhPt;\n                $this->w = $this->fw;\n                $this->h = $this->fh;\n                if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {\n                    $this->tMargin = $this->orig_tMargin;\n                    $this->bMargin = $this->orig_bMargin;\n                    $this->DeflMargin = $this->orig_lMargin;\n                    $this->DefrMargin = $this->orig_rMargin;\n                    $this->margin_header = $this->orig_hMargin;\n                    $this->margin_footer = $this->orig_fMargin;\n                } else {\n                    $resetHTMLHeadersrequired = true;\n                }\n            } else {\n                $this->wPt = $this->fhPt;\n                $this->hPt = $this->fwPt;\n                $this->w = $this->fh;\n                $this->h = $this->fw;\n\n                if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {\n                    $this->tMargin = $this->orig_lMargin;\n                    $this->bMargin = $this->orig_rMargin;\n                    $this->DeflMargin = $this->orig_bMargin;\n                    $this->DefrMargin = $this->orig_tMargin;\n                    $this->margin_header = $this->orig_hMargin;\n                    $this->margin_footer = $this->orig_fMargin;\n                } else {\n                    $resetHTMLHeadersrequired = true;\n                }\n            }\n\n            $this->CurOrientation = $orientation;\n            $this->ResetMargins();\n            $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n            $this->PageBreakTrigger = $this->h - $this->bMargin;\n        }\n\n        $this->pageDim[$this->page]['w'] = $this->w;\n        $this->pageDim[$this->page]['h'] = $this->h;\n\n        $this->pageDim[$this->page]['outer_width_LR'] = isset($this->page_box['outer_width_LR']) ? $this->page_box['outer_width_LR'] : 0;\n        $this->pageDim[$this->page]['outer_width_TB'] = isset($this->page_box['outer_width_TB']) ? $this->page_box['outer_width_TB'] : 0;\n\n        if (!isset($this->page_box['outer_width_LR']) && !isset($this->page_box['outer_width_TB'])) {\n            $this->pageDim[$this->page]['bleedMargin'] = 0;\n        } elseif ($this->bleedMargin <= $this->page_box['outer_width_LR'] && $this->bleedMargin <= $this->page_box['outer_width_TB']) {\n            $this->pageDim[$this->page]['bleedMargin'] = $this->bleedMargin;\n        } else {\n            $this->pageDim[$this->page]['bleedMargin'] = min($this->page_box['outer_width_LR'], $this->page_box['outer_width_TB']) - 0.01;\n        }\n\n        // If Page Margins are re-defined\n        // strlen()>0 is used to pick up (integer) 0, (string) '0', or set value\n        if ((strlen($mgl) > 0 && $this->DeflMargin != $mgl) || (strlen($mgr) > 0 && $this->DefrMargin != $mgr) || (strlen($mgt) > 0 && $this->tMargin != $mgt) || (strlen($mgb) > 0 && $this->bMargin != $mgb) || (strlen($mgh) > 0 && $this->margin_header != $mgh) || (strlen($mgf) > 0 && $this->margin_footer != $mgf)) {\n\n            if (strlen($mgl) > 0) {\n                $this->DeflMargin = $mgl;\n            }\n\n            if (strlen($mgr) > 0) {\n                $this->DefrMargin = $mgr;\n            }\n\n            if (strlen($mgt) > 0) {\n                $this->tMargin = $mgt;\n            }\n\n            if (strlen($mgb) > 0) {\n                $this->bMargin = $mgb;\n            }\n\n            if (strlen($mgh) > 0) {\n                $this->margin_header = $mgh;\n            }\n\n            if (strlen($mgf) > 0) {\n                $this->margin_footer = $mgf;\n            }\n\n            $this->ResetMargins();\n            $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);\n\n            $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n            $resetHTMLHeadersrequired = true;\n        }\n\n        $this->ResetMargins();\n        $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n        $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);\n\n        // Reset column top margin\n        $this->y0 = $this->tMargin;\n\n        $this->x = $this->lMargin;\n        $this->y = $this->tMargin;\n        $this->FontFamily = '';\n\n        // HEADERS AND FOOTERS\t// mPDF 6\n        if ($ohvalue < 0 || strtoupper($ohvalue) == 'OFF') {\n            $this->HTMLHeader = '';\n            $resetHTMLHeadersrequired = true;\n        } elseif ($ohname && $ohvalue > 0) {\n            if (preg_match('/^html_(.*)$/i', $ohname, $n)) {\n                $name = $n[1];\n            } else {\n                $name = $ohname;\n            }\n            if (isset($this->pageHTMLheaders[$name])) {\n                $this->HTMLHeader = $this->pageHTMLheaders[$name];\n            } else {\n                $this->HTMLHeader = '';\n            }\n            $resetHTMLHeadersrequired = true;\n        }\n\n        if ($ehvalue < 0 || strtoupper($ehvalue) == 'OFF') {\n            $this->HTMLHeaderE = '';\n            $resetHTMLHeadersrequired = true;\n        } elseif ($ehname && $ehvalue > 0) {\n            if (preg_match('/^html_(.*)$/i', $ehname, $n)) {\n                $name = $n[1];\n            } else {\n                $name = $ehname;\n            }\n            if (isset($this->pageHTMLheaders[$name])) {\n                $this->HTMLHeaderE = $this->pageHTMLheaders[$name];\n            } else {\n                $this->HTMLHeaderE = '';\n            }\n            $resetHTMLHeadersrequired = true;\n        }\n\n        if ($ofvalue < 0 || strtoupper($ofvalue) == 'OFF') {\n            $this->HTMLFooter = '';\n            $resetHTMLHeadersrequired = true;\n        } elseif ($ofname && $ofvalue > 0) {\n            if (preg_match('/^html_(.*)$/i', $ofname, $n)) {\n                $name = $n[1];\n            } else {\n                $name = $ofname;\n            }\n            if (isset($this->pageHTMLfooters[$name])) {\n                $this->HTMLFooter = $this->pageHTMLfooters[$name];\n            } else {\n                $this->HTMLFooter = '';\n            }\n            $resetHTMLHeadersrequired = true;\n        }\n\n        if ($efvalue < 0 || strtoupper($efvalue) == 'OFF') {\n            $this->HTMLFooterE = '';\n            $resetHTMLHeadersrequired = true;\n        } elseif ($efname && $efvalue > 0) {\n            if (preg_match('/^html_(.*)$/i', $efname, $n)) {\n                $name = $n[1];\n            } else {\n                $name = $efname;\n            }\n            if (isset($this->pageHTMLfooters[$name])) {\n                $this->HTMLFooterE = $this->pageHTMLfooters[$name];\n            } else {\n                $this->HTMLFooterE = '';\n            }\n            $resetHTMLHeadersrequired = true;\n        }\n\n        if ($resetHTMLHeadersrequired) {\n            $this->SetHTMLHeader($this->HTMLHeader);\n            $this->SetHTMLHeader($this->HTMLHeaderE, 'E');\n            $this->SetHTMLFooter($this->HTMLFooter);\n            $this->SetHTMLFooter($this->HTMLFooterE, 'E');\n        }\n\n\n        if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN\n            $this->_setAutoHeaderHeight($this->HTMLHeaderE);\n            $this->_setAutoFooterHeight($this->HTMLFooterE);\n        } else { // ODD or DEFAULT\n            $this->_setAutoHeaderHeight($this->HTMLHeader);\n            $this->_setAutoFooterHeight($this->HTMLFooter);\n        }\n\n        // Reset column top margin\n        $this->y0 = $this->tMargin;\n\n        $this->x = $this->lMargin;\n        $this->y = $this->tMargin;\n    }\n\n    // mPDF 6\n    function _setAutoHeaderHeight(&$htmlh)\n    {\n        /* When the setAutoTopMargin option is set to pad/stretch, only apply auto header height when a header exists */\n        if ($this->HTMLHeader === '' && $this->HTMLHeaderE === '') {\n            return;\n        }\n\n        if ($this->setAutoTopMargin == 'pad') {\n            if (isset($htmlh['h']) && $htmlh['h']) {\n                $h = $htmlh['h'];\n            } // 5.7.3\n            else {\n                $h = 0;\n            }\n            $this->tMargin = $this->margin_header + $h + $this->orig_tMargin;\n        } elseif ($this->setAutoTopMargin == 'stretch') {\n            if (isset($htmlh['h']) && $htmlh['h']) {\n                $h = $htmlh['h'];\n            } // 5.7.3\n            else {\n                $h = 0;\n            }\n            $this->tMargin = max($this->orig_tMargin, $this->margin_header + $h + $this->autoMarginPadding);\n        }\n    }\n\n    // mPDF 6\n    function _setAutoFooterHeight(&$htmlf)\n    {\n        /* When the setAutoTopMargin option is set to pad/stretch, only apply auto footer height when a footer exists */\n        if ($this->HTMLFooter === '' && $this->HTMLFooterE === '') {\n            return;\n        }\n\n        if ($this->setAutoBottomMargin == 'pad') {\n            if (isset($htmlf['h']) && $htmlf['h']) {\n                $h = $htmlf['h'];\n            } // 5.7.3\n            else {\n                $h = 0;\n            }\n            $this->bMargin = $this->margin_footer + $h + $this->orig_bMargin;\n            $this->PageBreakTrigger = $this->h - $this->bMargin;\n        } elseif ($this->setAutoBottomMargin == 'stretch') {\n            if (isset($htmlf['h']) && $htmlf['h']) {\n                $h = $htmlf['h'];\n            } // 5.7.3\n            else {\n                $h = 0;\n            }\n            $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $h + $this->autoMarginPadding);\n            $this->PageBreakTrigger = $this->h - $this->bMargin;\n        }\n    }\n\n    function _endpage()\n    {\n        /* -- CSS-IMAGE-FLOAT -- */\n        $this->printfloatbuffer();\n        /* -- END CSS-IMAGE-FLOAT -- */\n\n        if ($this->visibility != 'visible') {\n            $this->SetVisibility('visible');\n        }\n        $this->EndLayer();\n        // End of page contents\n        $this->state = 1;\n    }\n\n    function _dounderline($x, $y, $txt, $OTLdata = false, $textvar = 0)\n    {\n        // Now print line exactly where $y secifies - called from Text() and Cell() - adjust  position there\n        // WORD SPACING\n        $w = ($this->GetStringWidth($txt, false, $OTLdata, $textvar) * Mpdf::SCALE) + ($this->charspacing * mb_strlen($txt, $this->mb_enc)) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc));\n        // Draw a line\n        return sprintf('%.3F %.3F m %.3F %.3F l S', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, ($x * Mpdf::SCALE) + $w, ($this->h - $y) * Mpdf::SCALE);\n    }\n\n\n\n    /* -- WATERMARK -- */\n\n    // add a watermark\n    function watermark($texte, $angle = 45, $fontsize = 96, $alpha = 0.2)\n    {\n        if ($this->PDFA || $this->PDFX) {\n            throw new \\Mpdf\\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');\n        }\n\n        if (!$this->watermark_font) {\n            $this->watermark_font = $this->default_font;\n        }\n\n        $this->SetFont($this->watermark_font, \"B\", $fontsize, false); // Don't output\n        $texte = $this->purify_utf8_text($texte);\n\n        if ($this->text_input_as_HTML) {\n            $texte = $this->all_entities_to_utf8($texte);\n        }\n\n        if ($this->usingCoreFont) {\n            $texte = mb_convert_encoding($texte, $this->mb_enc, 'UTF-8');\n        }\n\n        // DIRECTIONALITY\n        if (preg_match(\"/([\" . $this->pregRTLchars . \"])/u\", $texte)) {\n            $this->biDirectional = true;\n        } // *OTL*\n\n        $textvar = 0;\n        $save_OTLtags = $this->OTLtags;\n        $this->OTLtags = [];\n        if ($this->useKerning) {\n            if ($this->CurrentFont['haskernGPOS']) {\n                $this->OTLtags['Plus'] .= ' kern';\n            } else {\n                $textvar = ($textvar | TextVars::FC_KERNING);\n            }\n        }\n\n        /* -- OTL -- */\n        // Use OTL OpenType Table Layout - GSUB & GPOS\n        if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n            $texte = $this->otl->applyOTL($texte, $this->CurrentFont['useOTL']);\n            $OTLdata = $this->otl->OTLdata;\n        }\n        /* -- END OTL -- */\n        $this->OTLtags = $save_OTLtags;\n\n        $this->magic_reverse_dir($texte, $this->directionality, $OTLdata);\n\n        $this->SetAlpha($alpha);\n\n        $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n\n        $szfont = $fontsize;\n        $loop = 0;\n        $maxlen = (min($this->w, $this->h) ); // sets max length of text as 7/8 width/height of page\n\n        while ($loop == 0) {\n            $this->SetFont($this->watermark_font, \"B\", $szfont, false); // Don't output\n            $offset = ((sin(deg2rad($angle))) * ($szfont / Mpdf::SCALE));\n\n            $strlen = $this->GetStringWidth($texte, true, $OTLdata, $textvar);\n            if ($strlen > $maxlen - $offset) {\n                $szfont --;\n            } else {\n                $loop ++;\n            }\n        }\n\n        $this->SetFont($this->watermark_font, \"B\", $szfont - 0.1, true, true); // Output The -0.1 is because SetFont above is not written to PDF\n\n        // Repeating it will not output anything as mPDF thinks it is set\n        $adj = ((cos(deg2rad($angle))) * ($strlen / 2));\n        $opp = ((sin(deg2rad($angle))) * ($strlen / 2));\n\n        $wx = ($this->w / 2) - $adj + $offset / 3;\n        $wy = ($this->h / 2) + $opp;\n\n        $this->Rotate($angle, $wx, $wy);\n        $this->Text($wx, $wy, $texte, $OTLdata, $textvar);\n        $this->Rotate(0);\n        $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n\n        $this->SetAlpha(1);\n    }\n\n    function watermarkImg($src, $alpha = 0.2)\n    {\n        if ($this->PDFA || $this->PDFX) {\n            throw new \\Mpdf\\MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');\n        }\n\n        if ($this->watermarkImgBehind) {\n            $this->watermarkImgAlpha = $this->SetAlpha($alpha, 'Normal', true);\n        } else {\n            $this->SetAlpha($alpha, $this->watermarkImgAlphaBlend);\n        }\n\n        $this->Image($src, 0, 0, 0, 0, '', '', true, true, true);\n\n        if (!$this->watermarkImgBehind) {\n            $this->SetAlpha(1);\n        }\n    }\n\n    /* -- END WATERMARK -- */\n\n    function Rotate($angle, $x = -1, $y = -1)\n    {\n        if ($x == -1) {\n            $x = $this->x;\n        }\n        if ($y == -1) {\n            $y = $this->y;\n        }\n        if ($this->angle != 0) {\n            $this->writer->write('Q');\n        }\n        $this->angle = $angle;\n        if ($angle != 0) {\n            $angle*=M_PI / 180;\n            $c = cos($angle);\n            $s = sin($angle);\n            $cx = $x * Mpdf::SCALE;\n            $cy = ($this->h - $y) * Mpdf::SCALE;\n            $this->writer->write(sprintf('q %.5F %.5F %.5F %.5F %.3F %.3F cm 1 0 0 1 %.3F %.3F cm', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy));\n        }\n    }\n\n    function CircularText($x, $y, $r, $text, $align = 'top', $fontfamily = '', $fontsize = 0, $fontstyle = '', $kerning = 120, $fontwidth = 100, $divider = '')\n    {\n        if (empty($this->directWrite)) {\n            $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);\n        }\n\n        $this->directWrite->CircularText($x, $y, $r, $text, $align, $fontfamily, $fontsize, $fontstyle, $kerning, $fontwidth, $divider);\n    }\n\n    // From Invoice\n    function RoundedRect($x, $y, $w, $h, $r, $style = '')\n    {\n        $hp = $this->h;\n\n        if ($style == 'F') {\n            $op = 'f';\n        } elseif ($style == 'FD' or $style == 'DF') {\n            $op = 'B';\n        } else {\n            $op = 'S';\n        }\n\n        $MyArc = 4 / 3 * (sqrt(2) - 1);\n        $this->writer->write(sprintf('%.3F %.3F m', ($x + $r) * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));\n        $xc = $x + $w - $r;\n        $yc = $y + $r;\n        $this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - $y) * Mpdf::SCALE));\n\n        $this->_Arc($xc + $r * $MyArc, $yc - $r, $xc + $r, $yc - $r * $MyArc, $xc + $r, $yc);\n        $xc = $x + $w - $r;\n        $yc = $y + $h - $r;\n        $this->writer->write(sprintf('%.3F %.3F l', ($x + $w) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));\n\n        $this->_Arc($xc + $r, $yc + $r * $MyArc, $xc + $r * $MyArc, $yc + $r, $xc, $yc + $r);\n        $xc = $x + $r;\n        $yc = $y + $h - $r;\n        $this->writer->write(sprintf('%.3F %.3F l', $xc * Mpdf::SCALE, ($hp - ($y + $h)) * Mpdf::SCALE));\n\n        $this->_Arc($xc - $r * $MyArc, $yc + $r, $xc - $r, $yc + $r * $MyArc, $xc - $r, $yc);\n        $xc = $x + $r;\n        $yc = $y + $r;\n        $this->writer->write(sprintf('%.3F %.3F l', ($x) * Mpdf::SCALE, ($hp - $yc) * Mpdf::SCALE));\n\n        $this->_Arc($xc - $r, $yc - $r * $MyArc, $xc - $r * $MyArc, $yc - $r, $xc, $yc - $r);\n        $this->writer->write($op);\n    }\n\n    function _Arc($x1, $y1, $x2, $y2, $x3, $y3)\n    {\n        $h = $this->h;\n        $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x1 * Mpdf::SCALE, ($h - $y1) * Mpdf::SCALE, $x2 * Mpdf::SCALE, ($h - $y2) * Mpdf::SCALE, $x3 * Mpdf::SCALE, ($h - $y3) * Mpdf::SCALE));\n    }\n\n    // ====================================================\n\n\n\n    /* -- DIRECTW -- */\n    function Shaded_box($text, $font = '', $fontstyle = 'B', $szfont = '', $width = '70%', $style = 'DF', $radius = 2.5, $fill = '#FFFFFF', $color = '#000000', $pad = 2)\n    {\n        // F (shading - no line),S (line, no shading),DF (both)\n        if (empty($this->directWrite)) {\n            $this->directWrite = new DirectWrite($this, $this->otl, $this->sizeConverter, $this->colorConverter);\n        }\n        $this->directWrite->Shaded_box($text, $font, $fontstyle, $szfont, $width, $style, $radius, $fill, $color, $pad);\n    }\n\n    /* -- END DIRECTW -- */\n\n    function UTF8StringToArray($str, $addSubset = true)\n    {\n        $out = [];\n        $len = strlen($str);\n        for ($i = 0; $i < $len; $i++) {\n            $uni = -1;\n            $h = ord($str[$i]);\n            if ($h <= 0x7F) {\n                $uni = $h;\n            } elseif ($h >= 0xC2) {\n                if (($h <= 0xDF) && ($i < $len - 1)) {\n                    $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);\n                } elseif (($h <= 0xEF) && ($i < $len - 2)) {\n                    $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);\n                } elseif (($h <= 0xF4) && ($i < $len - 3)) {\n                    $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);\n                }\n            }\n            if ($uni >= 0) {\n                $out[] = $uni;\n                if ($addSubset && isset($this->CurrentFont['subset'])) {\n                    $this->CurrentFont['subset'][$uni] = $uni;\n                }\n            }\n        }\n        return $out;\n    }\n\n    // Convert utf-8 string to <HHHHHH> for Font Subsets\n    function UTF8toSubset($str)\n    {\n        $ret = '<';\n        // $str = preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $str );\t// mPDF 6 deleted\n        // $str = preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $str );\t// mPDF 6 deleted\n        $unicode = $this->UTF8StringToArray($str);\n        $orig_fid = $this->CurrentFont['subsetfontids'][0];\n        $last_fid = $this->CurrentFont['subsetfontids'][0];\n        foreach ($unicode as $c) {\n            /* \t// mPDF 6 deleted\n\t\t\t  if ($c == 7 || $c == 8) {\n\t\t\t  if ($orig_fid != $last_fid) {\n\t\t\t  $ret .= '> Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <';\n\t\t\t  $last_fid = $orig_fid;\n\t\t\t  }\n\t\t\t  if ($c == 7) { $ret .= $this->aliasNbPgHex; }\n\t\t\t  else { $ret .= $this->aliasNbPgGpHex; }\n\t\t\t  continue;\n\t\t\t  }\n\t\t\t */\n            if (!$this->_charDefined($this->CurrentFont['cw'], $c)) {\n                $c = 0;\n            } // mPDF 6\n            for ($i = 0; $i < 99; $i++) {\n                // return c as decimal char\n                $init = array_search($c, $this->CurrentFont['subsets'][$i]);\n                if ($init !== false) {\n                    if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {\n                        $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';\n                        $last_fid = $this->CurrentFont['subsetfontids'][$i];\n                    }\n                    $ret .= sprintf(\"%02s\", strtoupper(dechex($init)));\n                    break;\n                } // TrueType embedded SUBSETS\n                elseif (count($this->CurrentFont['subsets'][$i]) < 255) {\n                    $n = count($this->CurrentFont['subsets'][$i]);\n                    $this->CurrentFont['subsets'][$i][$n] = $c;\n                    if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {\n                        $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';\n                        $last_fid = $this->CurrentFont['subsetfontids'][$i];\n                    }\n                    $ret .= sprintf(\"%02s\", strtoupper(dechex($n)));\n                    break;\n                } elseif (!isset($this->CurrentFont['subsets'][($i + 1)])) {\n                    // TrueType embedded SUBSETS\n                    $this->CurrentFont['subsets'][($i + 1)] = [0 => 0];\n                    $new_fid = count($this->fonts) + $this->extraFontSubsets + 1;\n                    $this->CurrentFont['subsetfontids'][($i + 1)] = $new_fid;\n                    $this->extraFontSubsets++;\n                }\n            }\n        }\n        $ret .= '>';\n        if ($last_fid != $orig_fid) {\n            $ret .= ' Tj /F' . $orig_fid . ' ' . $this->FontSizePt . ' Tf <> ';\n        }\n        return $ret;\n    }\n\n    /* -- CJK-FONTS -- */\n\n    // from class PDF_Chinese CJK EXTENSIONS\n    function AddCIDFont($family, $style, $name, &$cw, $CMap, $registry, $desc)\n    {\n        $fontkey = strtolower($family) . strtoupper($style);\n        if (isset($this->fonts[$fontkey])) {\n            throw new \\Mpdf\\MpdfException(\"Font already added: $family $style\");\n        }\n        $i = count($this->fonts) + $this->extraFontSubsets + 1;\n        $name = str_replace(' ', '', $name);\n        if ($family == 'sjis') {\n            $up = -120;\n        } else {\n            $up = -130;\n        }\n        // ? 'up' and 'ut' do not seem to be referenced anywhere\n        $this->fonts[$fontkey] = ['i' => $i, 'type' => 'Type0', 'name' => $name, 'up' => $up, 'ut' => 40, 'cw' => $cw, 'CMap' => $CMap, 'registry' => $registry, 'MissingWidth' => 1000, 'desc' => $desc];\n    }\n\n    function AddCJKFont($family)\n    {\n\n        if ($this->PDFA || $this->PDFX) {\n            throw new \\Mpdf\\MpdfException(\"Adobe CJK fonts cannot be embedded in mPDF (required for PDFA1-b and PDFX/1-a).\");\n        }\n        if ($family == 'big5') {\n            $this->AddBig5Font();\n        } elseif ($family == 'gb') {\n            $this->AddGBFont();\n        } elseif ($family == 'sjis') {\n            $this->AddSJISFont();\n        } elseif ($family == 'uhc') {\n            $this->AddUHCFont();\n        }\n    }\n\n    function AddBig5Font()\n    {\n        // Add Big5 font with proportional Latin\n        $family = 'big5';\n        $name = 'MSungStd-Light-Acro';\n        $cw = $this->Big5_widths;\n        $CMap = 'UniCNS-UTF16-H';\n        $registry = ['ordering' => 'CNS1', 'supplement' => 4];\n        $desc = [\n            'Ascent' => 880,\n            'Descent' => -120,\n            'CapHeight' => 880,\n            'Flags' => 6,\n            'FontBBox' => '[-160 -249 1015 1071]',\n            'ItalicAngle' => 0,\n            'StemV' => 93,\n        ];\n        $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);\n    }\n\n    function AddGBFont()\n    {\n        // Add GB font with proportional Latin\n        $family = 'gb';\n        $name = 'STSongStd-Light-Acro';\n        $cw = $this->GB_widths;\n        $CMap = 'UniGB-UTF16-H';\n        $registry = ['ordering' => 'GB1', 'supplement' => 4];\n        $desc = [\n            'Ascent' => 880,\n            'Descent' => -120,\n            'CapHeight' => 737,\n            'Flags' => 6,\n            'FontBBox' => '[-25 -254 1000 880]',\n            'ItalicAngle' => 0,\n            'StemV' => 58,\n            'Style' => '<< /Panose <000000000400000000000000> >>',\n        ];\n        $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);\n    }\n\n    function AddSJISFont()\n    {\n        // Add SJIS font with proportional Latin\n        $family = 'sjis';\n        $name = 'KozMinPro-Regular-Acro';\n        $cw = $this->SJIS_widths;\n        $CMap = 'UniJIS-UTF16-H';\n        $registry = ['ordering' => 'Japan1', 'supplement' => 5];\n        $desc = [\n            'Ascent' => 880,\n            'Descent' => -120,\n            'CapHeight' => 740,\n            'Flags' => 6,\n            'FontBBox' => '[-195 -272 1110 1075]',\n            'ItalicAngle' => 0,\n            'StemV' => 86,\n            'XHeight' => 502,\n        ];\n        $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);\n    }\n\n    function AddUHCFont()\n    {\n        // Add UHC font with proportional Latin\n        $family = 'uhc';\n        $name = 'HYSMyeongJoStd-Medium-Acro';\n        $cw = $this->UHC_widths;\n        $CMap = 'UniKS-UTF16-H';\n        $registry = ['ordering' => 'Korea1', 'supplement' => 2];\n        $desc = [\n            'Ascent' => 880,\n            'Descent' => -120,\n            'CapHeight' => 720,\n            'Flags' => 6,\n            'FontBBox' => '[-28 -148 1001 880]',\n            'ItalicAngle' => 0,\n            'StemV' => 60,\n            'Style' => '<< /Panose <000000000600000000000000> >>',\n        ];\n        $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);\n        $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);\n    }\n\n    /* -- END CJK-FONTS -- */\n\n    //////////////////////////////////////////////////////////////////////////////\n    //////////////////////////////////////////////////////////////////////////////\n    //////////////////////////////////////////////////////////////////////////////\n    //////////////////////////////////////////////////////////////////////////////\n    //////////////////////////////////////////////////////////////////////////////\n    //////////////////////////////////////////////////////////////////////////////\n    //////////////////////////////////////////////////////////////////////////////\n\n    function SetDefaultFont($font)\n    {\n        // Disallow embedded fonts to be used as defaults in PDFA\n        if ($this->PDFA || $this->PDFX) {\n            if (strtolower($font) == 'ctimes') {\n                $font = 'serif';\n            }\n            if (strtolower($font) == 'ccourier') {\n                $font = 'monospace';\n            }\n            if (strtolower($font) == 'chelvetica') {\n                $font = 'sans-serif';\n            }\n        }\n        $font = $this->SetFont($font); // returns substituted font if necessary\n        $this->default_font = $font;\n        $this->original_default_font = $font;\n        if (!$this->watermark_font) {\n            $this->watermark_font = $font;\n        } // *WATERMARK*\n        $this->defaultCSS['BODY']['FONT-FAMILY'] = $font;\n        $this->cssManager->CSS['BODY']['FONT-FAMILY'] = $font;\n    }\n\n    function SetDefaultFontSize($fontsize)\n    {\n        $this->default_font_size = $fontsize;\n        $this->original_default_font_size = $fontsize;\n        $this->SetFontSize($fontsize);\n        $this->defaultCSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';\n        $this->cssManager->CSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';\n    }\n\n    function SetDefaultBodyCSS($prop, $val)\n    {\n        if ($prop) {\n            $this->defaultCSS['BODY'][strtoupper($prop)] = $val;\n            $this->cssManager->CSS['BODY'][strtoupper($prop)] = $val;\n        }\n    }\n\n    function SetDirectionality($dir = 'ltr')\n    {\n        /* -- OTL -- */\n        if (strtolower($dir) == 'rtl') {\n            if ($this->directionality != 'rtl') {\n                // Swop L/R Margins so page 1 RTL is an 'even' page\n                $tmp = $this->DeflMargin;\n                $this->DeflMargin = $this->DefrMargin;\n                $this->DefrMargin = $tmp;\n                $this->orig_lMargin = $this->DeflMargin;\n                $this->orig_rMargin = $this->DefrMargin;\n\n                $this->SetMargins($this->DeflMargin, $this->DefrMargin, $this->tMargin);\n            }\n            $this->directionality = 'rtl';\n            $this->defaultAlign = 'R';\n            $this->defaultTableAlign = 'R';\n        } else {\n            /* -- END OTL -- */\n            $this->directionality = 'ltr';\n            $this->defaultAlign = 'L';\n            $this->defaultTableAlign = 'L';\n        } // *OTL*\n        $this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;\n    }\n\n    // Return either a number (factor) - based on current set fontsize (if % or em) - or exact lineheight (with 'mm' after it)\n    function fixLineheight($v)\n    {\n        $lh = false;\n        if (preg_match('/^[0-9\\.,]*$/', $v) && $v >= 0) {\n            return ($v + 0);\n        } elseif (strtoupper($v) == 'NORMAL' || $v == 'N') {\n            return 'N';  // mPDF 6\n        } else {\n            $tlh = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize, true);\n            if ($tlh) {\n                return ($tlh . 'mm');\n            }\n        }\n        return $this->normalLineheight;\n    }\n\n    function _getNormalLineheight($desc = false)\n    {\n        if (!$desc) {\n            $desc = $this->CurrentFont['desc'];\n        }\n        if (!isset($desc['Leading'])) {\n            $desc['Leading'] = 0;\n        }\n        if ($this->useFixedNormalLineHeight) {\n            $lh = $this->normalLineheight;\n        } elseif (isset($desc['Ascent']) && $desc['Ascent']) {\n            $lh = ($this->adjustFontDescLineheight * ($desc['Ascent'] - $desc['Descent'] + $desc['Leading']) / 1000);\n        } else {\n            $lh = $this->normalLineheight;\n        }\n        return $lh;\n    }\n\n    // Set a (fixed) lineheight to an actual value - either to named fontsize(pts) or default\n    function SetLineHeight($FontPt = '', $lh = '')\n    {\n        if (!$FontPt) {\n            $FontPt = $this->FontSizePt;\n        }\n        $fs = $FontPt / Mpdf::SCALE;\n        $this->lineheight = $this->_computeLineheight($lh, $fs);\n    }\n\n    function _computeLineheight($lh, $fs = '')\n    {\n        if ($this->shrin_k > 1) {\n            $k = $this->shrin_k;\n        } else {\n            $k = 1;\n        }\n        if (!$fs) {\n            $fs = $this->FontSize;\n        }\n        if ($lh == 'N') {\n            $lh = $this->_getNormalLineheight();\n        }\n        if (preg_match('/mm/', $lh)) {\n            return (((float) $lh) / $k); // convert to number\n        } elseif ($lh > 0) {\n            return ($fs * $lh);\n        }\n        return ($fs * $this->normalLineheight);\n    }\n\n    function _setLineYpos(&$fontsize, &$fontdesc, &$CSSlineheight, $blockYpos = false)\n    {\n        $ypos['glyphYorigin'] = 0;\n        $ypos['baseline-shift'] = 0;\n        $linegap = 0;\n        $leading = 0;\n\n        if (isset($fontdesc['Ascent']) && $fontdesc['Ascent'] && !$this->useFixedTextBaseline) {\n            // Fontsize uses font metrics - this method seems to produce results compatible with browsers (except IE9)\n            $ypos['boxtop'] = $fontdesc['Ascent'] / 1000 * $fontsize;\n            $ypos['boxbottom'] = $fontdesc['Descent'] / 1000 * $fontsize;\n            if (isset($fontdesc['Leading'])) {\n                $linegap = $fontdesc['Leading'] / 1000 * $fontsize;\n            }\n        } // Default if not set - uses baselineC\n        else {\n            $ypos['boxtop'] = (0.5 + $this->baselineC) * $fontsize;\n            $ypos['boxbottom'] = -(0.5 - $this->baselineC) * $fontsize;\n        }\n        $fontheight = $ypos['boxtop'] - $ypos['boxbottom'];\n\n        if ($this->shrin_k > 1) {\n            $shrin_k = $this->shrin_k;\n        } else {\n            $shrin_k = 1;\n        }\n\n        $leading = 0;\n        if ($CSSlineheight == 'N') {\n            $lh = $this->_getNormalLineheight($fontdesc);\n            $lineheight = ($fontsize * $lh);\n            $leading += $linegap; // specified in hhea or sTypo in OpenType tables\n        } elseif (preg_match('/mm/', $CSSlineheight)) {\n            $lineheight = (((float) $CSSlineheight) / $shrin_k); // convert to number\n        } // ??? If lineheight is a factor e.g. 1.3  ?? use factor x 1em or ? use 'normal' lineheight * factor\n        // Could depend on value for $text_height - a draft CSS value as set above for now\n        elseif ($CSSlineheight > 0) {\n            $lineheight = ($fontsize * $CSSlineheight);\n        } else {\n            $lineheight = ($fontsize * $this->normalLineheight);\n        }\n\n        // In general, calculate the \"leading\" - the difference between the fontheight and the lineheight\n        // and add half to the top and half to the bottom. BUT\n        // If an inline element has a font-size less than the block element, and the line-height is set as an em or % value\n        // it will add too much leading below the font and expand the height of the line - so just use the block element exttop/extbottom:\n        if (preg_match('/mm/', $CSSlineheight)\n            && ($blockYpos && $ypos['boxtop'] < $blockYpos['boxtop'])\n            && ($blockYpos && $ypos['boxbottom'] > $blockYpos['boxbottom'])) {\n\n            $ypos['exttop'] = $blockYpos['exttop'];\n            $ypos['extbottom'] = $blockYpos['extbottom'];\n\n        } else {\n\n            $leading += ($lineheight - $fontheight);\n\n            $ypos['exttop'] = $ypos['boxtop'] + $leading / 2;\n            $ypos['extbottom'] = $ypos['boxbottom'] - $leading / 2;\n        }\n\n\n        // TEMP ONLY FOR DEBUGGING *********************************\n        // $ypos['lineheight'] = $lineheight;\n        // $ypos['fontheight'] = $fontheight;\n        // $ypos['leading'] = $leading;\n\n        return $ypos;\n    }\n\n    /* Called from WriteFlowingBlock() and finishFlowingBlock()\n\t  Determines the line hieght and glyph/writing position\n\t  for each element in the line to be written */\n\n    function _setInlineBlockHeights(&$lineBox, &$stackHeight, &$content, &$font, $is_table)\n    {\n        if ($this->shrin_k > 1) {\n            $shrin_k = $this->shrin_k;\n        } else {\n            $shrin_k = 1;\n        }\n\n        $ypos = [];\n        $bordypos = [];\n        $bgypos = [];\n\n        if ($is_table) {\n            // FOR TABLE\n            $fontsize = $this->FontSize;\n            $fontkey = $this->FontFamily . $this->FontStyle;\n            $fontdesc = $this->fonts[$fontkey]['desc'];\n            $CSSlineheight = $this->cellLineHeight;\n            $line_stacking_strategy = $this->cellLineStackingStrategy; // inline-line-height [default] | block-line-height | max-height | grid-height\n            $line_stacking_shift = $this->cellLineStackingShift;  // consider-shifts [default] | disregard-shifts\n        } else {\n            // FOR BLOCK FONT\n            $fontsize = $this->blk[$this->blklvl]['InlineProperties']['size'];\n            $fontkey = $this->blk[$this->blklvl]['InlineProperties']['family'] . $this->blk[$this->blklvl]['InlineProperties']['style'];\n            $fontdesc = $this->fonts[$fontkey]['desc'];\n            $CSSlineheight = $this->blk[$this->blklvl]['line_height'];\n            // inline-line-height | block-line-height | max-height | grid-height\n            $line_stacking_strategy = (isset($this->blk[$this->blklvl]['line_stacking_strategy']) ? $this->blk[$this->blklvl]['line_stacking_strategy'] : 'inline-line-height');\n            // consider-shifts | disregard-shifts\n            $line_stacking_shift = (isset($this->blk[$this->blklvl]['line_stacking_shift']) ? $this->blk[$this->blklvl]['line_stacking_shift'] : 'consider-shifts');\n        }\n        $boxLineHeight = $this->_computeLineheight($CSSlineheight, $fontsize);\n\n\n        // First, set a \"strut\" using block font at index $lineBox[-1]\n        $ypos[-1] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight);\n\n        // for the block element - always taking the block EXTENDED progression including leading - which may be negative\n        if ($line_stacking_strategy == 'block-line-height') {\n            $topy = $ypos[-1]['exttop'];\n            $bottomy = $ypos[-1]['extbottom'];\n        } else {\n            $topy = 0;\n            $bottomy = 0;\n        }\n\n        // Get text-middle for aligning images/objects\n        $midpoint = $ypos[-1]['boxtop'] - (($ypos[-1]['boxtop'] - $ypos[-1]['boxbottom']) / 2);\n\n        // for images / inline objects / replaced elements\n        $mta = 0; // Maximum top-aligned\n        $mba = 0; // Maximum bottom-aligned\n        foreach ($content as $k => $chunk) {\n            if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'listmarker') {\n                $ypos[$k] = $ypos[-1];\n                // UPDATE Maximums\n                if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements\n                    if ($ypos[$k]['boxtop'] > $topy) {\n                        $topy = $ypos[$k]['boxtop'];\n                    }\n                    if ($ypos[$k]['boxbottom'] < $bottomy) {\n                        $bottomy = $ypos[$k]['boxbottom'];\n                    }\n                } else {\n                    if ($ypos[$k]['exttop'] > $topy) {\n                        $topy = $ypos[$k]['exttop'];\n                    }\n                    if ($ypos[$k]['extbottom'] < $bottomy) {\n                        $bottomy = $ypos[$k]['extbottom'];\n                    }\n                }\n            } elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB\n                $fontsize = $font[$k]['size'];\n                $fontdesc = $font[$k]['curr']['desc'];\n                $lh = 1;\n                $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $lh, $ypos[-1]); // Lineheight=1 fixed\n            } elseif (isset($this->objectbuffer[$k])) {\n                $oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];\n                $va = $this->objectbuffer[$k]['vertical-align'];\n\n                if ($va == 'BS') { //  (BASELINE default)\n                    if ($oh > $topy) {\n                        $topy = $oh;\n                    }\n                } elseif ($va == 'M') {\n                    if (($midpoint + $oh / 2) > $topy) {\n                        $topy = $midpoint + $oh / 2;\n                    }\n                    if (($midpoint - $oh / 2) < $bottomy) {\n                        $bottomy = $midpoint - $oh / 2;\n                    }\n                } elseif ($va == 'TT') {\n                    if (($ypos[-1]['boxtop'] - $oh) < $bottomy) {\n                        $bottomy = $ypos[-1]['boxtop'] - $oh;\n                        $topy = max($topy, $ypos[-1]['boxtop']);\n                    }\n                } elseif ($va == 'TB') {\n                    if (($ypos[-1]['boxbottom'] + $oh) > $topy) {\n                        $topy = $ypos[-1]['boxbottom'] + $oh;\n                        $bottomy = min($bottomy, $ypos[-1]['boxbottom']);\n                    }\n                } elseif ($va == 'T') {\n                    if ($oh > $mta) {\n                        $mta = $oh;\n                    }\n                } elseif ($va == 'B') {\n                    if ($oh > $mba) {\n                        $mba = $oh;\n                    }\n                }\n            } elseif ($content[$k] || $content[$k] === '0') {\n                // FOR FLOWING BLOCK\n                $fontsize = $font[$k]['size'];\n                $fontdesc = $font[$k]['curr']['desc'];\n                // In future could set CSS line-height from inline elements; for now, use block level:\n                $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight, $ypos[-1]);\n\n                if (isset($font[$k]['textparam']['text-baseline']) && $font[$k]['textparam']['text-baseline'] != 0) {\n                    $ypos[$k]['baseline-shift'] = $font[$k]['textparam']['text-baseline'];\n                }\n\n                // DO ALIGNMENT FOR BASELINES *******************\n                // Until most fonts have OpenType BASE tables, this won't work\n                // $ypos[$k] compared to $ypos[-1] or $ypos[$k-1] using $dominant_baseline and $baseline_table\n                // UPDATE Maximums\n                if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements\n                    if ($line_stacking_shift == 'disregard-shifts') {\n                        if ($ypos[$k]['boxtop'] > $topy) {\n                            $topy = $ypos[$k]['boxtop'];\n                        }\n                        if ($ypos[$k]['boxbottom'] < $bottomy) {\n                            $bottomy = $ypos[$k]['boxbottom'];\n                        }\n                    } else {\n                        if (($ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift']) > $topy) {\n                            $topy = $ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift'];\n                        }\n                        if (($ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {\n                            $bottomy = $ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift'];\n                        }\n                    }\n                } else {\n                    if ($line_stacking_shift == 'disregard-shifts') {\n                        if ($ypos[$k]['exttop'] > $topy) {\n                            $topy = $ypos[$k]['exttop'];\n                        }\n                        if ($ypos[$k]['extbottom'] < $bottomy) {\n                            $bottomy = $ypos[$k]['extbottom'];\n                        }\n                    } else {\n                        if (($ypos[$k]['exttop'] + $ypos[$k]['baseline-shift']) > $topy) {\n                            $topy = $ypos[$k]['exttop'] + $ypos[$k]['baseline-shift'];\n                        }\n                        if (($ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift']) < $bottomy) {\n                            $bottomy = $ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift'];\n                        }\n                    }\n                }\n\n                // If BORDER set on inline element\n                if (isset($font[$k]['bord']) && $font[$k]['bord']) {\n                    $bordfontsize = $font[$k]['textparam']['bord-decoration']['fontsize'] / $shrin_k;\n                    $bordfontkey = $font[$k]['textparam']['bord-decoration']['fontkey'];\n                    if ($bordfontkey != $fontkey || $bordfontsize != $fontsize || isset($font[$k]['textparam']['bord-decoration']['baseline'])) {\n                        $bordfontdesc = $this->fonts[$bordfontkey]['desc'];\n                        $bordypos[$k] = $this->_setLineYpos($bordfontsize, $bordfontdesc, $CSSlineheight, $ypos[-1]);\n                        if (isset($font[$k]['textparam']['bord-decoration']['baseline']) && $font[$k]['textparam']['bord-decoration']['baseline'] != 0) {\n                            $bordypos[$k]['baseline-shift'] = $font[$k]['textparam']['bord-decoration']['baseline'] / $shrin_k;\n                        }\n                    }\n                }\n                // If BACKGROUND set on inline element\n                if (isset($font[$k]['spanbgcolor']) && $font[$k]['spanbgcolor']) {\n                    $bgfontsize = $font[$k]['textparam']['bg-decoration']['fontsize'] / $shrin_k;\n                    $bgfontkey = $font[$k]['textparam']['bg-decoration']['fontkey'];\n                    if ($bgfontkey != $fontkey || $bgfontsize != $fontsize || isset($font[$k]['textparam']['bg-decoration']['baseline'])) {\n                        $bgfontdesc = $this->fonts[$bgfontkey]['desc'];\n                        $bgypos[$k] = $this->_setLineYpos($bgfontsize, $bgfontdesc, $CSSlineheight, $ypos[-1]);\n                        if (isset($font[$k]['textparam']['bg-decoration']['baseline']) && $font[$k]['textparam']['bg-decoration']['baseline'] != 0) {\n                            $bgypos[$k]['baseline-shift'] = $font[$k]['textparam']['bg-decoration']['baseline'] / $shrin_k;\n                        }\n                    }\n                }\n            }\n        }\n\n\n        // TOP or BOTTOM aligned images\n        if ($mta > ($topy - $bottomy)) {\n            if (($topy - $mta) < $bottomy) {\n                $bottomy = $topy - $mta;\n            }\n        }\n        if ($mba > ($topy - $bottomy)) {\n            if (($bottomy + $mba) > $topy) {\n                $topy = $bottomy + $mba;\n            }\n        }\n\n        if ($line_stacking_strategy == 'block-line-height') { // fixed height set by block element (whether present or not)\n            $topy = $ypos[-1]['exttop'];\n            $bottomy = $ypos[-1]['extbottom'];\n        }\n\n        $inclusiveHeight = $topy - $bottomy;\n\n        // SET $stackHeight taking note of line_stacking_strategy\n        // NB inclusive height already takes account of need to consider block progression height (excludes leading set by lineheight)\n        // or extended block progression height (includes leading set by lineheight)\n        if ($line_stacking_strategy == 'block-line-height') { // fixed = extended block progression height of block element\n            $stackHeight = $boxLineHeight;\n        } elseif ($line_stacking_strategy == 'max-height') { // smallest height which includes extended block progression height of block element\n            // and block progression heights of inline elements (NOT extended)\n            $stackHeight = $inclusiveHeight;\n        } elseif ($line_stacking_strategy == 'grid-height') { // smallest multiple of block element lineheight to include\n            // block progression heights of inline elements (NOT extended)\n            $stackHeight = $boxLineHeight;\n            while ($stackHeight < $inclusiveHeight) {\n                $stackHeight += $boxLineHeight;\n            }\n        } else { // 'inline-line-height' = default\t\t// smallest height which includes extended block progression height of block element\n            // AND extended block progression heights of inline elements\n            $stackHeight = $inclusiveHeight;\n        }\n\n        $diff = $stackHeight - $inclusiveHeight;\n        $topy += $diff / 2;\n        $bottomy -= $diff / 2;\n\n        // ADJUST $ypos => lineBox using $stackHeight; lineBox are all offsets from the top of stackHeight in mm\n        // and SET IMAGE OFFSETS\n        $lineBox[-1]['boxtop'] = $topy - $ypos[-1]['boxtop'];\n        $lineBox[-1]['boxbottom'] = $topy - $ypos[-1]['boxbottom'];\n        // $lineBox[-1]['exttop'] = $topy - $ypos[-1]['exttop'];\n        // $lineBox[-1]['extbottom'] = $topy - $ypos[-1]['extbottom'];\n        $lineBox[-1]['glyphYorigin'] = $topy - $ypos[-1]['glyphYorigin'];\n        $lineBox[-1]['baseline-shift'] = $ypos[-1]['baseline-shift'];\n\n        $midpoint = $lineBox[-1]['boxbottom'] - (($lineBox[-1]['boxbottom'] - $lineBox[-1]['boxtop']) / 2);\n\n        foreach ($content as $k => $chunk) {\n            if (isset($this->objectbuffer[$k])) {\n                $oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];\n                // LIST MARKERS\n                if ($this->objectbuffer[$k]['type'] == 'listmarker') {\n                    $oh = $fontsize;\n                } elseif ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB\n                    $oh = $font[$k]['size']; // == $this->objectbuffer[$k]['fontsize']/Mpdf::SCALE;\n                    $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];\n                    $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];\n                    $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];\n                    $lineBox[$k]['baseline-shift'] = 0;\n                    // continue;\n                }\n                $va = $this->objectbuffer[$k]['vertical-align']; // = $objattr['vertical-align'] = set as M,T,B,S\n\n                if ($va == 'BS') { //  (BASELINE default)\n                    $lineBox[$k]['top'] = $lineBox[-1]['glyphYorigin'] - $oh;\n                } elseif ($va == 'M') {\n                    $lineBox[$k]['top'] = $midpoint - $oh / 2;\n                } elseif ($va == 'TT') {\n                    $lineBox[$k]['top'] = $lineBox[-1]['boxtop'];\n                } elseif ($va == 'TB') {\n                    $lineBox[$k]['top'] = $lineBox[-1]['boxbottom'] - $oh;\n                } elseif ($va == 'T') {\n                    $lineBox[$k]['top'] = 0;\n                } elseif ($va == 'B') {\n                    $lineBox[$k]['top'] = $stackHeight - $oh;\n                }\n            } elseif ($content[$k] || $content[$k] === '0') {\n                $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];\n                $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];\n                // $lineBox[$k]['exttop'] = $topy - $ypos[$k]['exttop'];\n                // $lineBox[$k]['extbottom'] = $topy - $ypos[$k]['extbottom'];\n                $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];\n                $lineBox[$k]['baseline-shift'] = $ypos[$k]['baseline-shift'];\n                if (isset($bordypos[$k]['boxtop'])) {\n                    $lineBox[$k]['border-boxtop'] = $topy - $bordypos[$k]['boxtop'];\n                    $lineBox[$k]['border-boxbottom'] = $topy - $bordypos[$k]['boxbottom'];\n                    $lineBox[$k]['border-baseline-shift'] = $bordypos[$k]['baseline-shift'];\n                }\n                if (isset($bgypos[$k]['boxtop'])) {\n                    $lineBox[$k]['background-boxtop'] = $topy - $bgypos[$k]['boxtop'];\n                    $lineBox[$k]['background-boxbottom'] = $topy - $bgypos[$k]['boxbottom'];\n                    $lineBox[$k]['background-baseline-shift'] = $bgypos[$k]['baseline-shift'];\n                }\n            }\n        }\n    }\n\n    function SetBasePath($str = '')\n    {\n        if (isset($_SERVER['HTTP_HOST'])) {\n            $host = $_SERVER['HTTP_HOST'];\n        } elseif (isset($_SERVER['SERVER_NAME'])) {\n            $host = $_SERVER['SERVER_NAME'];\n        } else {\n            $host = '';\n        }\n        if (!$str) {\n            if (isset($_SERVER['SCRIPT_NAME'])) {\n                $currentPath = dirname($_SERVER['SCRIPT_NAME']);\n            } else {\n                $currentPath = dirname($_SERVER['PHP_SELF']);\n            }\n            $currentPath = str_replace(\"\\\\\", \"/\", $currentPath);\n            if ($currentPath == '/') {\n                $currentPath = '';\n            }\n            if ($host) {  // mPDF 6\n                if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') {\n                    $currpath = 'https://' . $host . $currentPath . '/';\n                } else {\n                    $currpath = 'http://' . $host . $currentPath . '/';\n                }\n            } else {\n                $currpath = '';\n            }\n            $this->basepath = $currpath;\n            $this->basepathIsLocal = true;\n            return;\n        }\n        $str = preg_replace('/\\?.*/', '', $str);\n        if (!preg_match('/(http|https|ftp):\\/\\/.*\\//i', $str)) {\n            $str .= '/';\n        }\n        $str .= 'xxx'; // in case $str ends in / e.g. http://www.bbc.co.uk/\n        $this->basepath = dirname($str) . \"/\"; // returns e.g. e.g. http://www.google.com/dir1/dir2/dir3/\n        $this->basepath = str_replace(\"\\\\\", \"/\", $this->basepath); // If on Windows\n        $tr = parse_url($this->basepath);\n        if (isset($tr['host']) && ($tr['host'] == $host)) {\n            $this->basepathIsLocal = true;\n        } else {\n            $this->basepathIsLocal = false;\n        }\n    }\n\n    public function GetFullPath(&$path, $basepath = '')\n    {\n        // When parsing CSS need to pass temporary basepath - so links are relative to current stylesheet\n        if (!$basepath) {\n            $basepath = $this->basepath;\n        }\n\n        // Fix path value\n        $path = str_replace(\"\\\\\", '/', $path); // If on Windows\n\n        // mPDF 5.7.2\n        if (substr($path, 0, 2) === '//') {\n            $scheme = parse_url($basepath, PHP_URL_SCHEME);\n            $scheme = $scheme ?: 'http';\n            $path = $scheme . ':' . $path;\n        }\n\n        $path = preg_replace('|^./|', '', $path); // Inadvertently corrects \"./path/etc\" and \"//www.domain.com/etc\"\n\n        if (substr($path, 0, 1) == '#') {\n            return;\n        }\n\n        if (preg_match('@^(mailto|tel|fax):.*@i', $path)) {\n            return;\n        }\n\n        if (substr($path, 0, 3) == \"../\") { // It is a relative link\n\n            $backtrackamount = substr_count($path, \"../\");\n            $maxbacktrack = substr_count($basepath, \"/\") - 3;\n            $filepath = str_replace(\"../\", '', $path);\n            $path = $basepath;\n\n            // If it is an invalid relative link, then make it go to directory root\n            if ($backtrackamount > $maxbacktrack) {\n                $backtrackamount = $maxbacktrack;\n            }\n\n            // Backtrack some directories\n            for ($i = 0; $i < $backtrackamount + 1; $i++) {\n                $path = substr($path, 0, strrpos($path, \"/\"));\n            }\n\n            $path = $path . \"/\" . $filepath; // Make it an absolute path\n\n        } elseif ((strpos($path, \":/\") === false || strpos($path, \":/\") > 10) && !is_file($path)) { // It is a local link\n\n            if (substr($path, 0, 1) == \"/\") {\n\n                $tr = parse_url($basepath);\n\n                // mPDF 5.7.2\n                $root = '';\n                if (!empty($tr['scheme'])) {\n                    $root .= $tr['scheme'] . '://';\n                }\n\n                $root .= isset($tr['host']) ? $tr['host'] : '';\n                $root .= ((isset($tr['port']) && $tr['port']) ? (':' . $tr['port']) : ''); // mPDF 5.7.3\n\n                $path = $root . $path;\n\n            } else {\n                $path = $basepath . $path;\n            }\n        }\n        // Do nothing if it is an Absolute Link\n    }\n\n    function docPageNum($num = 0, $extras = false)\n    {\n        if ($num < 1) {\n            $num = $this->page;\n        }\n\n        $type = $this->defaultPageNumStyle; // set default Page Number Style\n        $ppgno = $num;\n        $suppress = 0;\n        $offset = 0;\n        $lastreset = 0;\n\n        foreach ($this->PageNumSubstitutions as $psarr) {\n\n            if ($num >= $psarr['from']) {\n\n                if ($psarr['reset']) {\n                    if ($psarr['reset'] > 1) {\n                        $offset = $psarr['reset'] - 1;\n                    }\n                    $ppgno = $num - $psarr['from'] + 1 + $offset;\n                    $lastreset = $psarr['from'];\n                }\n\n                if ($psarr['type']) {\n                    $type = $psarr['type'];\n                }\n\n                if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {\n                    $suppress = 1;\n                } elseif (strtoupper($psarr['suppress']) == 'OFF') {\n                    $suppress = 0;\n                }\n            }\n        }\n\n        if ($suppress) {\n            return '';\n        }\n\n        $ppgno = $this->_getStyledNumber($ppgno, $type);\n\n        if ($extras) {\n            $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;\n        }\n\n        return $ppgno;\n    }\n\n    function docPageNumTotal($num = 0, $extras = false)\n    {\n        if ($num < 1) {\n            $num = $this->page;\n        }\n\n        $type = $this->defaultPageNumStyle; // set default Page Number Style\n        $ppgstart = 1;\n        $ppgend = count($this->pages) + 1;\n        $suppress = 0;\n        $offset = 0;\n\n        foreach ($this->PageNumSubstitutions as $psarr) {\n            if ($num >= $psarr['from']) {\n                if ($psarr['reset']) {\n                    if ($psarr['reset'] > 1) {\n                        $offset = $psarr['reset'] - 1;\n                    }\n                    $ppgstart = $psarr['from'] + $offset;\n                    $ppgend = count($this->pages) + 1 + $offset;\n                }\n                if ($psarr['type']) {\n                    $type = $psarr['type'];\n                }\n                if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {\n                    $suppress = 1;\n                } elseif (strtoupper($psarr['suppress']) == 'OFF') {\n                    $suppress = 0;\n                }\n            }\n            if ($num < $psarr['from']) {\n                if ($psarr['reset']) {\n                    $ppgend = $psarr['from'] + $offset;\n                    break;\n                }\n            }\n        }\n\n        if ($suppress) {\n            return '';\n        }\n\n        $ppgno = $ppgend - $ppgstart + $offset;\n        $ppgno = $this->_getStyledNumber($ppgno, $type);\n\n        if ($extras) {\n            $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;\n        }\n\n        return $ppgno;\n    }\n\n    // mPDF 6\n    function _getStyledNumber($ppgno, $type, $listmarker = false)\n    {\n        if ($listmarker) {\n            $reverse = true; // Reverse RTL numerals (Hebrew) when using for list\n            $checkfont = true; // Using list - font is set, so check if character is available\n        } else {\n            $reverse = false; // For pagenumbers, RTL numerals (Hebrew) will get reversed later by bidi\n            $checkfont = false; // For pagenumbers - font is not set, so no check\n        }\n\n        $decToAlpha = new Conversion\\DecToAlpha();\n        $decToCjk = new Conversion\\DecToCjk();\n        $decToHebrew = new Conversion\\DecToHebrew();\n        $decToRoman = new Conversion\\DecToRoman();\n        $decToOther = new Conversion\\DecToOther($this);\n\n        $lowertype = strtolower($type);\n\n        if ($lowertype == 'upper-latin' || $lowertype == 'upper-alpha' || $type == 'A') {\n\n            $ppgno = $decToAlpha->convert($ppgno, true);\n\n        } elseif ($lowertype == 'lower-latin' || $lowertype == 'lower-alpha' || $type == 'a') {\n\n            $ppgno = $decToAlpha->convert($ppgno, false);\n\n        } elseif ($lowertype == 'upper-roman' || $type == 'I') {\n\n            $ppgno = $decToRoman->convert($ppgno, true);\n\n        } elseif ($lowertype == 'lower-roman' || $type == 'i') {\n\n            $ppgno = $decToRoman->convert($ppgno, false);\n\n        } elseif ($lowertype == 'hebrew') {\n\n            $ppgno = $decToHebrew->convert($ppgno, $reverse);\n\n        } elseif (preg_match('/(arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao)/i', $lowertype, $m)) {\n\n            $cp = $decToOther->getCodePage($m[1]);\n            $ppgno = $decToOther->convert($ppgno, $cp, $checkfont);\n\n        } elseif ($lowertype == 'cjk-decimal') {\n\n            $ppgno = $decToCjk->convert($ppgno);\n\n        }\n\n        return $ppgno;\n    }\n\n    function docPageSettings($num = 0)\n    {\n        // Returns current type (numberstyle), suppression state for this page number;\n        // reset is only returned if set for this page number\n        if ($num < 1) {\n            $num = $this->page;\n        }\n\n        $type = $this->defaultPageNumStyle; // set default Page Number Style\n        $ppgno = $num;\n        $suppress = 0;\n        $offset = 0;\n        $reset = '';\n\n        foreach ($this->PageNumSubstitutions as $psarr) {\n            if ($num >= $psarr['from']) {\n                if ($psarr['reset']) {\n                    if ($psarr['reset'] > 1) {\n                        $offset = $psarr['reset'] - 1;\n                    }\n                    $ppgno = $num - $psarr['from'] + 1 + $offset;\n                }\n                if ($psarr['type']) {\n                    $type = $psarr['type'];\n                }\n                if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {\n                    $suppress = 1;\n                } elseif (strtoupper($psarr['suppress']) == 'OFF') {\n                    $suppress = 0;\n                }\n            }\n            if ($num == $psarr['from']) {\n                $reset = $psarr['reset'];\n            }\n        }\n\n        if ($suppress) {\n            $suppress = 'on';\n        } else {\n            $suppress = 'off';\n        }\n\n        return [$type, $suppress, $reset];\n    }\n\n    function RestartDocTemplate()\n    {\n        $this->docTemplateStart = $this->page;\n    }\n\n    // Page header\n    function Header($content = '')\n    {\n\n        $this->cMarginL = 0;\n        $this->cMarginR = 0;\n\n\n        if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLHeaderE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLHeader) || (!$this->mirrorMargins && $this->HTMLHeader)) {\n            $this->writeHTMLHeaders();\n            return;\n        }\n    }\n\n    /* -- TABLES -- */\n    function TableHeaderFooter($content = '', $tablestartpage = '', $tablestartcolumn = '', $horf = 'H', $level = 0, $firstSpread = true, $finalSpread = true)\n    {\n        if (($horf == 'H' || $horf == 'F') && !empty($content)) { // mPDF 5.7.2\n            $table = &$this->table[1][1];\n\n            // mPDF 5.7.2\n            if ($horf == 'F') { // Table Footer\n                $firstrow = count($table['cells']) - $table['footernrows'];\n                $lastrow = count($table['cells']) - 1;\n            } else {  // Table Header\n                $firstrow = 0;\n                $lastrow = $table['headernrows'] - 1;\n            }\n            if (empty($content[$firstrow])) {\n                if ($this->debug) {\n                    throw new \\Mpdf\\MpdfException(\"<tfoot> must precede <tbody> in a table\");\n                } else {\n                    return;\n                }\n            }\n\n\n            // Advance down page by half width of top border\n            if ($horf == 'H') { // Only if header\n                if ($table['borders_separate']) {\n                    $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];\n                } else {\n                    $adv = $table['max_cell_border_width']['T'] / 2;\n                }\n                if ($adv) {\n                    if ($this->table_rotate) {\n                        $this->y += ($adv);\n                    } else {\n                        $this->DivLn($adv, $this->blklvl, true);\n                    }\n                }\n            }\n\n            $topy = $content[$firstrow][0]['y'] - $this->y;\n\n            for ($i = $firstrow; $i <= $lastrow; $i++) {\n                $y = $this->y;\n\n                /* -- COLUMNS -- */\n                // If outside columns, this is done in PaintDivBB\n                if ($this->ColActive) {\n                    // OUTER FILL BGCOLOR of DIVS\n                    if ($this->blklvl > 0) {\n                        $firstblockfill = $this->GetFirstBlockFill();\n                        if ($firstblockfill && $this->blklvl >= $firstblockfill) {\n                            $divh = $content[$i][0]['h'];\n                            $bak_x = $this->x;\n                            $this->DivLn($divh, -3, false);\n                            // Reset current block fill\n                            $bcor = $this->blk[$this->blklvl]['bgcolorarray'];\n                            $this->SetFColor($bcor);\n                            $this->x = $bak_x;\n                        }\n                    }\n                }\n                /* -- END COLUMNS -- */\n\n                $colctr = 0;\n                foreach ($content[$i] as $tablehf) {\n                    $colctr++;\n                    $y = Arrays::get($tablehf, 'y', null) - $topy;\n                    $this->y = $y;\n                    // Set some cell values\n                    $x = Arrays::get($tablehf, 'x', null);\n                    if (($this->mirrorMargins) && ($tablestartpage == 'ODD') && (($this->page) % 2 == 0)) { // EVEN\n                        $x = $x + $this->MarginCorrection;\n                    } elseif (($this->mirrorMargins) && ($tablestartpage == 'EVEN') && (($this->page) % 2 == 1)) { // ODD\n                        $x = $x + $this->MarginCorrection;\n                    }\n                    /* -- COLUMNS -- */\n                    // Added to correct for Columns\n                    if ($this->ColActive) {\n                        if ($this->directionality == 'rtl') { // *OTL*\n                            $x -= ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*\n                        } // *OTL*\n                        else { // *OTL*\n                            $x += ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap);\n                        } // *OTL*\n                    }\n                    /* -- END COLUMNS -- */\n\n                    if ($colctr == 1) {\n                        $x0 = $x;\n                    }\n\n                    // mPDF ITERATION\n                    if ($this->iterationCounter) {\n                        foreach ($tablehf['textbuffer'] as $k => $t) {\n                            if (!is_array($t[0]) && preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {\n                                $vname = '__' . $m[1] . '_';\n                                if (!isset($this->$vname)) {\n                                    $this->$vname = 1;\n                                } else {\n                                    $this->$vname++;\n                                }\n                                $tablehf['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $tablehf['textbuffer'][$k][0]);\n                            }\n                        }\n                    }\n\n                    $w = Arrays::get($tablehf, 'w', null);\n                    $h = Arrays::get($tablehf, 'h', null);\n                    $va = Arrays::get($tablehf, 'va', null);\n                    $R = Arrays::get($tablehf, 'R', null);\n                    $direction = Arrays::get($tablehf, 'direction', null);\n                    $mih = Arrays::get($tablehf, 'mih', null);\n                    $border = Arrays::get($tablehf, 'border', null);\n                    $border_details = Arrays::get($tablehf, 'border_details', null);\n                    $padding = Arrays::get($tablehf, 'padding', null);\n                    $this->tabletheadjustfinished = true;\n\n                    $textbuffer = Arrays::get($tablehf, 'textbuffer', null);\n\n                    // Align\n                    $align = Arrays::get($tablehf, 'a', null);\n                    $this->cellTextAlign = $align;\n\n                    $this->cellLineHeight = Arrays::get($tablehf, 'cellLineHeight', null);\n                    $this->cellLineStackingStrategy = Arrays::get($tablehf, 'cellLineStackingStrategy', null);\n                    $this->cellLineStackingShift = Arrays::get($tablehf, 'cellLineStackingShift', null);\n\n                    $this->x = $x;\n\n                    if ($this->ColActive) {\n                        if ($table['borders_separate']) {\n                            $tablefill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;\n                            if ($tablefill) {\n                                $color = $this->colorConverter->convert($tablefill, $this->PDFAXwarnings);\n                                if ($color) {\n                                    $xadj = ($table['border_spacing_H'] / 2);\n                                    $yadj = ($table['border_spacing_V'] / 2);\n                                    $wadj = $table['border_spacing_H'];\n                                    $hadj = $table['border_spacing_V'];\n                                    if ($i == $firstrow && $horf == 'H') {  // Top\n                                        $yadj += $table['padding']['T'] + $table['border_details']['T']['w'];\n                                        $hadj += $table['padding']['T'] + $table['border_details']['T']['w'];\n                                    }\n                                    if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1)) || (!isset($tablehf['rowspan']) && ($i + 1) == ($lastrow + 1))) && $horf == 'F') { // Bottom\n                                        $hadj += $table['padding']['B'] + $table['border_details']['B']['w'];\n                                    }\n                                    if ($colctr == 1) {  // Left\n                                        $xadj += $table['padding']['L'] + $table['border_details']['L']['w'];\n                                        $wadj += $table['padding']['L'] + $table['border_details']['L']['w'];\n                                    }\n                                    if ($colctr == count($content[$i])) { // Right\n                                        $wadj += $table['padding']['R'] + $table['border_details']['R']['w'];\n                                    }\n                                    $this->SetFColor($color);\n                                    $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');\n                                }\n                            }\n                        }\n                    }\n\n                    if ($table['empty_cells'] != 'hide' || !empty($textbuffer) || !$table['borders_separate']) {\n                        $paintcell = true;\n                    } else {\n                        $paintcell = false;\n                    }\n\n                    // Vertical align\n                    if ($R && intval($R) > 0 && isset($va) && $va != 'B') {\n                        $va = 'B';\n                    }\n\n                    if (!isset($va) || empty($va) || $va == 'M') {\n                        $this->y += ($h - $mih) / 2;\n                    } elseif (isset($va) && $va == 'B') {\n                        $this->y += $h - $mih;\n                    }\n\n\n                    // TABLE ROW OR CELL FILL BGCOLOR\n                    $fill = 0;\n                    if (isset($tablehf['bgcolor']) && $tablehf['bgcolor'] && $tablehf['bgcolor'] != 'transparent') {\n                        $fill = $tablehf['bgcolor'];\n                        $leveladj = 6;\n                    } elseif (isset($content[$i][0]['trbgcolor']) && $content[$i][0]['trbgcolor'] && $content[$i][0]['trbgcolor'] != 'transparent') { // Row color\n                        $fill = $content[$i][0]['trbgcolor'];\n                        $leveladj = 3;\n                    }\n                    if ($fill && $paintcell) {\n                        $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);\n                        if ($color) {\n                            if ($table['borders_separate']) {\n                                if ($this->ColActive) {\n                                    $this->SetFColor($color);\n                                    $this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F');\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color];\n                                }\n                            } else {\n                                if ($this->ColActive) {\n                                    $this->SetFColor($color);\n                                    $this->Rect($x, $y, $w, $h, 'F');\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];\n                                }\n                            }\n                        }\n                    }\n\n\n                    /* -- BACKGROUNDS -- */\n                    if (isset($tablehf['gradient']) && $tablehf['gradient'] && $paintcell) {\n                        $g = $this->gradient->parseBackgroundGradient($tablehf['gradient']);\n                        if ($g) {\n                            if ($table['borders_separate']) {\n                                $px = $x + ($table['border_spacing_H'] / 2);\n                                $py = $y + ($table['border_spacing_V'] / 2);\n                                $pw = $w - $table['border_spacing_H'];\n                                $ph = $h - $table['border_spacing_V'];\n                            } else {\n                                $px = $x;\n                                $py = $y;\n                                $pw = $w;\n                                $ph = $h;\n                            }\n                            if ($this->ColActive) {\n                                $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);\n                            } else {\n                                $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                            }\n                        }\n                    }\n\n                    if (isset($tablehf['background-image']) && $paintcell) {\n                        if ($tablehf['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $tablehf['background-image']['gradient'])) {\n                            $g = $this->gradient->parseMozGradient($tablehf['background-image']['gradient']);\n                            if ($g) {\n                                if ($table['borders_separate']) {\n                                    $px = $x + ($table['border_spacing_H'] / 2);\n                                    $py = $y + ($table['border_spacing_V'] / 2);\n                                    $pw = $w - $table['border_spacing_H'];\n                                    $ph = $h - $table['border_spacing_V'];\n                                } else {\n                                    $px = $x;\n                                    $py = $y;\n                                    $pw = $w;\n                                    $ph = $h;\n                                }\n                                if ($this->ColActive) {\n                                    $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                }\n                            }\n                        } elseif ($tablehf['background-image']['image_id']) { // Background pattern\n                            $n = count($this->patterns) + 1;\n                            if ($table['borders_separate']) {\n                                $px = $x + ($table['border_spacing_H'] / 2);\n                                $py = $y + ($table['border_spacing_V'] / 2);\n                                $pw = $w - $table['border_spacing_H'];\n                                $ph = $h - $table['border_spacing_V'];\n                            } else {\n                                $px = $x;\n                                $py = $y;\n                                $pw = $w;\n                                $ph = $h;\n                            }\n                            if ($this->ColActive) {\n                                [$orig_w, $orig_h, $x_repeat, $y_repeat] = $this->_resizeBackgroundImage($tablehf['background-image']['orig_w'], $tablehf['background-image']['orig_h'], $pw, $ph, $tablehf['background-image']['resize'], $tablehf['background-image']['x_repeat'], $tablehf['background-image']['y_repeat']);\n                                $this->patterns[$n] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $tablehf['background-image']['itype']];\n                                if ($tablehf['background-image']['opacity'] > 0 && $tablehf['background-image']['opacity'] < 1) {\n                                    $opac = $this->SetAlpha($tablehf['background-image']['opacity'], 'Normal', true);\n                                } else {\n                                    $opac = '';\n                                }\n                                $this->writer->write(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * Mpdf::SCALE, ($this->h - $py) * Mpdf::SCALE, $pw * Mpdf::SCALE, -$ph * Mpdf::SCALE));\n                            } else {\n                                $this->tableBackgrounds[$level * 9 + 8][] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $tablehf['background-image']['orig_w'], 'orig_h' => $tablehf['background-image']['orig_h'], 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $tablehf['background-image']['x_repeat'], 'y_repeat' => $tablehf['background-image']['y_repeat'], 'clippath' => '', 'resize' => $tablehf['background-image']['resize'], 'opacity' => $tablehf['background-image']['opacity'], 'itype' => $tablehf['background-image']['itype']];\n                            }\n                        }\n                    }\n                    /* -- END BACKGROUNDS -- */\n\n                    // Cell Border\n                    if ($table['borders_separate'] && $paintcell && $border) {\n                        $this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($border_details['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($border_details['T']['w'] / 2), $w - $table['border_spacing_H'] - ($border_details['L']['w'] / 2) - ($border_details['R']['w'] / 2), $h - $table['border_spacing_V'] - ($border_details['T']['w'] / 2) - ($border_details['B']['w'] / 2), $border, $border_details, false, $table['borders_separate']);\n                    } elseif ($paintcell && $border) {\n                        $this->_tableRect($x, $y, $w, $h, $border, $border_details, true, $table['borders_separate']);   // true causes buffer\n                    }\n\n                    // Print cell content\n                    if (!empty($textbuffer)) {\n                        if ($horf == 'F' && preg_match('/{colsum([0-9]*)[_]*}/', $textbuffer[0][0], $m)) {\n                            $rep = sprintf(\"%01.\" . intval($m[1]) . \"f\", $this->colsums[$colctr - 1]);\n                            $textbuffer[0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $textbuffer[0][0]);\n                        }\n\n                        if ($R) {\n                            $cellPtSize = $textbuffer[0][11] / $this->shrin_k;\n                            if (!$cellPtSize) {\n                                $cellPtSize = $this->default_font_size;\n                            }\n                            $cellFontHeight = ($cellPtSize / Mpdf::SCALE);\n                            $opx = $this->x;\n                            $opy = $this->y;\n                            $angle = intval($R);\n\n                            // Only allow 45 - 90 degrees (when bottom-aligned) or -90\n                            if ($angle > 90) {\n                                $angle = 90;\n                            } elseif ($angle > 0 && (isset($va) && $va != 'B')) {\n                                $angle = 90;\n                            } elseif ($angle > 0 && $angle < 45) {\n                                $angle = 45;\n                            } elseif ($angle < 0) {\n                                $angle = -90;\n                            }\n\n                            $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);\n                            if (isset($align) && $align == 'R') {\n                                $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($padding['R'] + $border_details['R']['w']);\n                            } elseif (!isset($align) || $align == 'C') {\n                                $this->x += ($w / 2) + ($offset);\n                            } else {\n                                $this->x += ($offset) + ($cellFontHeight / 3) + ($padding['L'] + $border_details['L']['w']);\n                            }\n                            $str = '';\n                            foreach ($tablehf['textbuffer'] as $t) {\n                                $str .= $t[0] . ' ';\n                            }\n                            $str = rtrim($str);\n\n                            if (!isset($va) || $va == 'M') {\n                                $this->y -= ($h - $mih) / 2; // Undo what was added earlier VERTICAL ALIGN\n                                if ($angle > 0) {\n                                    $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']) + ($mih - ($padding['T'] + $border_details['T']['w'] + $border_details['B']['w'] + $padding['B']));\n                                } elseif ($angle < 0) {\n                                    $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']);\n                                }\n                            } elseif (isset($va) && $va == 'B') {\n                                $this->y -= $h - $mih; // Undo what was added earlier VERTICAL ALIGN\n                                if ($angle > 0) {\n                                    $this->y += $h - ($border_details['B']['w'] + $padding['B']);\n                                } elseif ($angle < 0) {\n                                    $this->y += $h - $mih + ($padding['T'] + $border_details['T']['w']);\n                                }\n                            } elseif (isset($va) && $va == 'T') {\n                                if ($angle > 0) {\n                                    $this->y += $mih - ($border_details['B']['w'] + $padding['B']);\n                                } elseif ($angle < 0) {\n                                    $this->y += ($padding['T'] + $border_details['T']['w']);\n                                }\n                            }\n\n                            $this->Rotate($angle, $this->x, $this->y);\n                            $s_fs = $this->FontSizePt;\n                            $s_f = $this->FontFamily;\n                            $s_st = $this->FontStyle;\n                            if (!empty($textbuffer[0][3])) { // Font Color\n                                $cor = $textbuffer[0][3];\n                                $this->SetTColor($cor);\n                            }\n                            $this->SetFont($textbuffer[0][4], $textbuffer[0][2], $cellPtSize, true, true);\n\n                            $this->magic_reverse_dir($str, $this->directionality, $textbuffer[0][18]);\n                            $this->Text($this->x, $this->y, $str, $textbuffer[0][18], $textbuffer[0][8]); // textvar\n                            $this->Rotate(0);\n                            $this->SetFont($s_f, $s_st, $s_fs, true, true);\n                            $this->SetTColor(0);\n                            $this->x = $opx;\n                            $this->y = $opy;\n                        } else {\n                            if ($table['borders_separate']) { // NB twice border width\n                                $xadj = $border_details['L']['w'] + $padding['L'] + ($table['border_spacing_H'] / 2);\n                                $wadj = $border_details['L']['w'] + $border_details['R']['w'] + $padding['L'] + $padding['R'] + $table['border_spacing_H'];\n                                $yadj = $border_details['T']['w'] + $padding['T'] + ($table['border_spacing_H'] / 2);\n                            } else {\n                                $xadj = $border_details['L']['w'] / 2 + $padding['L'];\n                                $wadj = ($border_details['L']['w'] + $border_details['R']['w']) / 2 + $padding['L'] + $padding['R'];\n                                $yadj = $border_details['T']['w'] / 2 + $padding['T'];\n                            }\n\n                            $this->divwidth = $w - ($wadj);\n                            $this->x += $xadj;\n                            $this->y += $yadj;\n                            $this->printbuffer($textbuffer, '', true, false, $direction);\n                        }\n                    }\n                    $textbuffer = [];\n\n                    /* -- BACKGROUNDS -- */\n                    if (!$this->ColActive) {\n                        if (isset($content[$i][0]['trgradients']) && ($colctr == 1 || $table['borders_separate'])) {\n                            $g = $this->gradient->parseBackgroundGradient($content[$i][0]['trgradients']);\n                            if ($g) {\n                                $gx = $x0;\n                                $gy = $y;\n                                $gh = $h;\n                                $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n                                if ($table['borders_separate']) {\n                                    $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);\n                                    $clx = $x + ($table['border_spacing_H'] / 2);\n                                    $cly = $y + ($table['border_spacing_V'] / 2);\n                                    $clw = $w - $table['border_spacing_H'];\n                                    $clh = $h - $table['border_spacing_V'];\n                                    // Set clipping path\n                                    $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6\n                                    $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                }\n                            }\n                        }\n\n                        if (isset($content[$i][0]['trbackground-images']) && ($colctr == 1 || $table['borders_separate'])) {\n                            if ($content[$i][0]['trbackground-images']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $content[$i][0]['trbackground-images']['gradient'])) {\n                                $g = $this->gradient->parseMozGradient($content[$i][0]['trbackground-images']['gradient']);\n                                if ($g) {\n                                    $gx = $x0;\n                                    $gy = $y;\n                                    $gh = $h;\n                                    $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n                                    if ($table['borders_separate']) {\n                                        $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);\n                                        $clx = $x + ($table['border_spacing_H'] / 2);\n                                        $cly = $y + ($table['border_spacing_V'] / 2);\n                                        $clw = $w - $table['border_spacing_H'];\n                                        $clh = $h - $table['border_spacing_V'];\n                                        // Set clipping path\n                                        $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6\n                                        $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];\n                                    } else {\n                                        $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                    }\n                                }\n                            } else {\n                                $image_id = $content[$i][0]['trbackground-images']['image_id'];\n                                $orig_w = $content[$i][0]['trbackground-images']['orig_w'];\n                                $orig_h = $content[$i][0]['trbackground-images']['orig_h'];\n                                $x_pos = $content[$i][0]['trbackground-images']['x_pos'];\n                                $y_pos = $content[$i][0]['trbackground-images']['y_pos'];\n                                $x_repeat = $content[$i][0]['trbackground-images']['x_repeat'];\n                                $y_repeat = $content[$i][0]['trbackground-images']['y_repeat'];\n                                $resize = $content[$i][0]['trbackground-images']['resize'];\n                                $opacity = $content[$i][0]['trbackground-images']['opacity'];\n                                $itype = $content[$i][0]['trbackground-images']['itype'];\n\n                                $clippath = '';\n                                $gx = $x0;\n                                $gy = $y;\n                                $gh = $h;\n                                $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n                                if ($table['borders_separate']) {\n                                    $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);\n                                    $clx = $x + ($table['border_spacing_H'] / 2);\n                                    $cly = $y + ($table['border_spacing_V'] / 2);\n                                    $clw = $w - $table['border_spacing_H'];\n                                    $clh = $h - $table['border_spacing_V'];\n                                    // Set clipping path\n                                    $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6\n                                    $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];\n                                }\n                            }\n                        }\n                    }\n                    /* -- END BACKGROUNDS -- */\n\n                    // TABLE BORDER - if separate OR collapsed and only table border\n                    if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {\n                        $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);\n                        $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);\n                        $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);\n                        $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);\n                        $tbx = $x;\n                        $tby = $y;\n                        $tbw = $w;\n                        $tbh = $h;\n                        $tab_bord = 0;\n                        $corner = '';\n                        if ($i == $firstrow && $horf == 'H') {  // Top\n                            $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);\n                            $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::TOP);\n                            $corner .= 'T';\n                        }\n                        if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1))) && $horf == 'F') { // Bottom\n                            $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::BOTTOM);\n                            $corner .= 'B';\n                        }\n                        if ($colctr == 1 && $firstSpread) { // Left\n                            $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);\n                            $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::LEFT);\n                            $corner .= 'L';\n                        }\n                        if ($colctr == count($content[$i]) && $finalSpread) { // Right\n                            $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::RIGHT);\n                            $corner .= 'R';\n                        }\n                        $this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']);\n                    }\n                }// end column $content\n                $this->y = $y + $h; // Update y coordinate\n            }// end row $i\n            unset($table);\n            $this->colsums = [];\n        }\n    }\n\n    /* -- END TABLES -- */\n\n    function SetHTMLHeader($header = '', $OE = '', $write = false)\n    {\n\n        $height = 0;\n        if (is_array($header) && isset($header['html']) && $header['html']) {\n            $Hhtml = $header['html'];\n            if ($this->setAutoTopMargin) {\n                if (isset($header['h'])) {\n                    $height = $header['h'];\n                } else {\n                    $height = $this->_getHtmlHeight($Hhtml);\n                }\n            }\n        } elseif (!is_array($header) && $header) {\n            $Hhtml = $header;\n            if ($this->setAutoTopMargin) {\n                $height = $this->_getHtmlHeight($Hhtml);\n            }\n        } else {\n            $Hhtml = '';\n        }\n\n        if ($OE !== 'E') {\n            $OE = 'O';\n        }\n\n        if ($OE === 'E') {\n            if ($Hhtml) {\n                $this->HTMLHeaderE = [];\n                $this->HTMLHeaderE['html'] = $Hhtml;\n                $this->HTMLHeaderE['h'] = $height;\n            } else {\n                $this->HTMLHeaderE = '';\n            }\n        } else {\n            if ($Hhtml) {\n                $this->HTMLHeader = [];\n                $this->HTMLHeader['html'] = $Hhtml;\n                $this->HTMLHeader['h'] = $height;\n            } else {\n                $this->HTMLHeader = '';\n            }\n        }\n\n        if (!$this->mirrorMargins && $OE == 'E') {\n            return;\n        }\n        if ($Hhtml == '') {\n            return;\n        }\n\n        if ($this->setAutoTopMargin == 'pad') {\n            $this->tMargin = $this->margin_header + $height + $this->orig_tMargin;\n            if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {\n                $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;\n            }\n        } elseif ($this->setAutoTopMargin == 'stretch') {\n            $this->tMargin = max($this->orig_tMargin, $this->margin_header + $height + $this->autoMarginPadding);\n            if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {\n                $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;\n            }\n        }\n        if ($write && $this->state != 0 && (($this->mirrorMargins && $OE == 'E' && ($this->page) % 2 == 0) || ($this->mirrorMargins && $OE != 'E' && ($this->page) % 2 == 1) || !$this->mirrorMargins)) {\n            $this->writeHTMLHeaders();\n        }\n    }\n\n    function SetHTMLFooter($footer = '', $OE = '')\n    {\n        $height = 0;\n        if (is_array($footer) && isset($footer['html']) && $footer['html']) {\n            $Fhtml = $footer['html'];\n            if ($this->setAutoBottomMargin) {\n                if (isset($footer['h'])) {\n                    $height = $footer['h'];\n                } else {\n                    $height = $this->_getHtmlHeight($Fhtml);\n                }\n            }\n        } elseif (!is_array($footer) && $footer) {\n            $Fhtml = $footer;\n            if ($this->setAutoBottomMargin) {\n                $height = $this->_getHtmlHeight($Fhtml);\n            }\n        } else {\n            $Fhtml = '';\n        }\n\n        if ($OE !== 'E') {\n            $OE = 'O';\n        }\n\n        if ($OE === 'E') {\n            if ($Fhtml) {\n                $this->HTMLFooterE = [];\n                $this->HTMLFooterE['html'] = $Fhtml;\n                $this->HTMLFooterE['h'] = $height;\n            } else {\n                $this->HTMLFooterE = '';\n            }\n        } else {\n            if ($Fhtml) {\n                $this->HTMLFooter = [];\n                $this->HTMLFooter['html'] = $Fhtml;\n                $this->HTMLFooter['h'] = $height;\n            } else {\n                $this->HTMLFooter = '';\n            }\n        }\n\n        if (!$this->mirrorMargins && $OE == 'E') {\n            return;\n        }\n\n        if ($Fhtml == '') {\n            return false;\n        }\n\n        if ($this->setAutoBottomMargin == 'pad') {\n            $this->bMargin = $this->margin_footer + $height + $this->orig_bMargin;\n            $this->PageBreakTrigger = $this->h - $this->bMargin;\n            if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {\n                $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;\n            }\n        } elseif ($this->setAutoBottomMargin == 'stretch') {\n            $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $height + $this->autoMarginPadding);\n            $this->PageBreakTrigger = $this->h - $this->bMargin;\n            if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {\n                $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;\n            }\n        }\n    }\n\n    function _getHtmlHeight($html)\n    {\n        $save_state = $this->state;\n        if ($this->state == 0) {\n            $this->AddPage($this->CurOrientation);\n        }\n        $this->state = 2;\n        $this->Reset();\n        $this->pageoutput[$this->page] = [];\n        $save_x = $this->x;\n        $save_y = $this->y;\n        $this->x = $this->lMargin;\n        $this->y = $this->margin_header;\n        $html = str_replace('{PAGENO}', $this->pagenumPrefix . $this->docPageNum($this->page) . $this->pagenumSuffix, $html);\n        $html = str_replace($this->aliasNbPgGp, $this->nbpgPrefix . $this->docPageNumTotal($this->page) . $this->nbpgSuffix, $html);\n        $html = str_replace($this->aliasNbPg, $this->page, $html);\n        $html = preg_replace_callback('/\\{DATE\\s+(.*?)\\}/', [$this, 'date_callback'], $html); // mPDF 5.7\n        $this->HTMLheaderPageLinks = [];\n        $this->HTMLheaderPageAnnots = [];\n        $this->HTMLheaderPageForms = [];\n        $savepb = $this->pageBackgrounds;\n        $this->writingHTMLheader = true;\n        $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);\n        $this->writingHTMLheader = false;\n        $h = ($this->y - $this->margin_header);\n        $this->Reset();\n        // mPDF 5.7.2 - Clear in case Float used in Header/Footer\n        $this->blk[0]['blockContext'] = 0;\n        $this->blk[0]['float_endpos'] = 0;\n\n        $this->pageoutput[$this->page] = [];\n        $this->headerbuffer = '';\n        $this->pageBackgrounds = $savepb;\n        $this->x = $save_x;\n        $this->y = $save_y;\n        $this->state = $save_state;\n        if ($save_state == 0) {\n            unset($this->pages[1]);\n            $this->page = 0;\n        }\n        return $h;\n    }\n\n    // Called internally from Header\n    function writeHTMLHeaders()\n    {\n\n        if ($this->mirrorMargins && ($this->page) % 2 == 0) {\n            $OE = 'E';\n        } else {\n            $OE = 'O';\n        }\n\n        if ($OE === 'E') {\n            $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeaderE['html'];\n        } else {\n            $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeader['html'];\n        }\n\n        if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {\n            $this->saveHTMLHeader[$this->page][$OE]['rotate'] = true;\n            $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->tMargin;\n            $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->bMargin;\n            $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;\n            $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;\n            $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->h;\n            $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->w;\n        } else {\n            $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->lMargin;\n            $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->rMargin;\n            $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;\n            $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;\n            $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->w;\n            $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->h;\n        }\n    }\n\n    function writeHTMLFooters()\n    {\n\n        if ($this->mirrorMargins && ($this->page) % 2 == 0) {\n            $OE = 'E';\n        } else {\n            $OE = 'O';\n        }\n\n        if ($OE === 'E') {\n            $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooterE['html'];\n        } else {\n            $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooter['html'];\n        }\n\n        if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {\n            $this->saveHTMLFooter[$this->page][$OE]['rotate'] = true;\n            $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->tMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->bMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->rMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->lMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;\n            $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;\n            $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->h;\n            $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->w;\n        } else {\n            $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->lMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->rMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->tMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->bMargin;\n            $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;\n            $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;\n            $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->w;\n            $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->h;\n        }\n    }\n\n    // mPDF 6\n    function _shareHeaderFooterWidth($cl, $cc, $cr)\n    {\n        // mPDF 6\n        $l = mb_strlen($cl, 'UTF-8');\n        $c = mb_strlen($cc, 'UTF-8');\n        $r = mb_strlen($cr, 'UTF-8');\n        $s = max($l, $r);\n        $tw = $c + 2 * $s;\n        if ($tw > 0) {\n            return [intval($s * 100 / $tw), intval($c * 100 / $tw), intval($s * 100 / $tw)];\n        } else {\n            return [33, 33, 33];\n        }\n    }\n\n    // mPDF 6\n    // Create an HTML header/footer from array (non-HTML header/footer)\n    function _createHTMLheaderFooter($arr, $hf)\n    {\n        $lContent = (isset($arr['L']['content']) ? $arr['L']['content'] : '');\n        $cContent = (isset($arr['C']['content']) ? $arr['C']['content'] : '');\n        $rContent = (isset($arr['R']['content']) ? $arr['R']['content'] : '');\n        [$lw, $cw, $rw] = $this->_shareHeaderFooterWidth($lContent, $cContent, $rContent);\n        if ($hf == 'H') {\n            $valign = 'bottom';\n            $vpadding = '0 0 ' . $this->header_line_spacing . 'em 0';\n        } else {\n            $valign = 'top';\n            $vpadding = '' . $this->footer_line_spacing . 'em 0 0 0';\n        }\n        if ($this->directionality == 'rtl') { // table columns get reversed so need different text-alignment\n            $talignL = 'right';\n            $talignR = 'left';\n        } else {\n            $talignL = 'left';\n            $talignR = 'right';\n        }\n        $html = '<table width=\"100%\" style=\"border-collapse: collapse; margin: 0; vertical-align: ' . $valign . '; color: #000000; ';\n        if (isset($arr['line']) && $arr['line']) {\n            $html .= ' border-' . $valign . ': 0.1mm solid #000000;';\n        }\n        $html .= '\">';\n        $html .= '<tr>';\n        $html .= '<td width=\"' . $lw . '%\" style=\"padding: ' . $vpadding . '; text-align: ' . $talignL . '; ';\n        if (isset($arr['L']['font-family'])) {\n            $html .= ' font-family: ' . $arr['L']['font-family'] . ';';\n        }\n        if (isset($arr['L']['color'])) {\n            $html .= ' color: ' . $arr['L']['color'] . ';';\n        }\n        if (isset($arr['L']['font-size'])) {\n            $html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';\n        }\n        if (isset($arr['L']['font-style'])) {\n            if ($arr['L']['font-style'] == 'B' || $arr['L']['font-style'] == 'BI') {\n                $html .= ' font-weight: bold;';\n            }\n            if ($arr['L']['font-style'] == 'I' || $arr['L']['font-style'] == 'BI') {\n                $html .= ' font-style: italic;';\n            }\n        }\n        $html .= '\">' . $lContent . '</td>';\n        $html .= '<td width=\"' . $cw . '%\" style=\"padding: ' . $vpadding . '; text-align: center; ';\n        if (isset($arr['C']['font-family'])) {\n            $html .= ' font-family: ' . $arr['C']['font-family'] . ';';\n        }\n        if (isset($arr['C']['color'])) {\n            $html .= ' color: ' . $arr['C']['color'] . ';';\n        }\n        if (isset($arr['C']['font-size'])) {\n            $html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';\n        }\n        if (isset($arr['C']['font-style'])) {\n            if ($arr['C']['font-style'] == 'B' || $arr['C']['font-style'] == 'BI') {\n                $html .= ' font-weight: bold;';\n            }\n            if ($arr['C']['font-style'] == 'I' || $arr['C']['font-style'] == 'BI') {\n                $html .= ' font-style: italic;';\n            }\n        }\n        $html .= '\">' . $cContent . '</td>';\n        $html .= '<td width=\"' . $rw . '%\" style=\"padding: ' . $vpadding . '; text-align: ' . $talignR . '; ';\n        if (isset($arr['R']['font-family'])) {\n            $html .= ' font-family: ' . $arr['R']['font-family'] . ';';\n        }\n        if (isset($arr['R']['color'])) {\n            $html .= ' color: ' . $arr['R']['color'] . ';';\n        }\n        if (isset($arr['R']['font-size'])) {\n            $html .= ' font-size: ' . $arr['R']['font-size'] . 'pt;';\n        }\n        if (isset($arr['R']['font-style'])) {\n            if ($arr['R']['font-style'] == 'B' || $arr['R']['font-style'] == 'BI') {\n                $html .= ' font-weight: bold;';\n            }\n            if ($arr['R']['font-style'] == 'I' || $arr['R']['font-style'] == 'BI') {\n                $html .= ' font-style: italic;';\n            }\n        }\n        $html .= '\">' . $rContent . '</td>';\n        $html .= '</tr></table>';\n        return $html;\n    }\n\n    function DefHeaderByName($name, $arr)\n    {\n        if (!$name) {\n            $name = '_nonhtmldefault';\n        }\n        $html = $this->_createHTMLheaderFooter($arr, 'H');\n\n        $this->pageHTMLheaders[$name]['html'] = $html;\n        $this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);\n    }\n\n    function DefFooterByName($name, $arr)\n    {\n        if (!$name) {\n            $name = '_nonhtmldefault';\n        }\n        $html = $this->_createHTMLheaderFooter($arr, 'F');\n\n        $this->pageHTMLfooters[$name]['html'] = $html;\n        $this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);\n    }\n\n    function SetHeaderByName($name, $side = 'O', $write = false)\n    {\n        if (!$name) {\n            $name = '_nonhtmldefault';\n        }\n        $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);\n    }\n\n    function SetFooterByName($name, $side = 'O')\n    {\n        if (!$name) {\n            $name = '_nonhtmldefault';\n        }\n        $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);\n    }\n\n    function DefHTMLHeaderByName($name, $html)\n    {\n        if (!$name) {\n            $name = '_default';\n        }\n\n        $this->pageHTMLheaders[$name]['html'] = $html;\n        $this->pageHTMLheaders[$name]['h'] = $this->_getHtmlHeight($html);\n    }\n\n    function DefHTMLFooterByName($name, $html)\n    {\n        if (!$name) {\n            $name = '_default';\n        }\n\n        $this->pageHTMLfooters[$name]['html'] = $html;\n        $this->pageHTMLfooters[$name]['h'] = $this->_getHtmlHeight($html);\n    }\n\n    function SetHTMLHeaderByName($name, $side = 'O', $write = false)\n    {\n        if (!$name) {\n            $name = '_default';\n        }\n        $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);\n    }\n\n    function SetHTMLFooterByName($name, $side = 'O')\n    {\n        if (!$name) {\n            $name = '_default';\n        }\n        $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);\n    }\n\n    function SetHeader($Harray = [], $side = '', $write = false)\n    {\n        $oddhtml = '';\n        $evenhtml = '';\n\n        if (is_string($Harray)) {\n\n            if (strlen($Harray) === 0) {\n\n                $oddhtml = '';\n                $evenhtml = '';\n\n            } elseif (strpos($Harray, '|') !== false) {\n\n                $hdet = explode('|', $Harray);\n\n                [$lw, $cw, $rw] = $this->_shareHeaderFooterWidth($hdet[0], $hdet[1], $hdet[2]);\n                $oddhtml = '<table width=\"100%\" style=\"border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';\n\n                if ($this->defaultheaderfontsize) {\n                    $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';\n                }\n\n                if ($this->defaultheaderfontstyle) {\n\n                    if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {\n                        $oddhtml .= ' font-weight: bold;';\n                    }\n\n                    if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {\n                        $oddhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultheaderline) {\n                    $oddhtml .= ' border-bottom: 0.1mm solid #000000;';\n                }\n\n                $oddhtml .= '\">';\n                $oddhtml .= '<tr>';\n                $oddhtml .= '<td width=\"' . $lw . '%\" style=\"padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; \">' . $hdet[0] . '</td>';\n                $oddhtml .= '<td width=\"' . $cw . '%\" style=\"padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; \">' . $hdet[1] . '</td>';\n                $oddhtml .= '<td width=\"' . $rw . '%\" style=\"padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; \">' . $hdet[2] . '</td>';\n                $oddhtml .= '</tr></table>';\n\n                $evenhtml = '<table width=\"100%\" style=\"border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';\n\n                if ($this->defaultheaderfontsize) {\n                    $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';\n                }\n\n                if ($this->defaultheaderfontstyle) {\n                    if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {\n                        $evenhtml .= ' font-weight: bold;';\n                    }\n                    if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {\n                        $evenhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultheaderline) {\n                    $evenhtml .= ' border-bottom: 0.1mm solid #000000;';\n                }\n\n                $evenhtml .= '\">';\n                $evenhtml .= '<tr>';\n                $evenhtml .= '<td width=\"' . $rw . '%\" style=\"padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; \">' . $hdet[2] . '</td>';\n                $evenhtml .= '<td width=\"' . $cw . '%\" style=\"padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; \">' . $hdet[1] . '</td>';\n                $evenhtml .= '<td width=\"' . $lw . '%\" style=\"padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; \">' . $hdet[0] . '</td>';\n                $evenhtml .= '</tr></table>';\n\n            } else {\n\n                $oddhtml = '<div style=\"margin: 0; color: #000000; ';\n\n                if ($this->defaultheaderfontsize) {\n                    $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';\n                }\n\n                if ($this->defaultheaderfontstyle) {\n\n                    if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {\n                        $oddhtml .= ' font-weight: bold;';\n                    }\n\n                    if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {\n                        $oddhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultheaderline) {\n                    $oddhtml .= ' border-bottom: 0.1mm solid #000000;';\n                }\n\n                $oddhtml .= 'text-align: right; \">' . $Harray . '</div>';\n                $evenhtml = '<div style=\"margin: 0; color: #000000; ';\n\n                if ($this->defaultheaderfontsize) {\n                    $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';\n                }\n\n                if ($this->defaultheaderfontstyle) {\n\n                    if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {\n                        $evenhtml .= ' font-weight: bold;';\n                    }\n\n                    if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {\n                        $evenhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultheaderline) {\n                    $evenhtml .= ' border-bottom: 0.1mm solid #000000;';\n                }\n\n                $evenhtml .= 'text-align: left; \">' . $Harray . '</div>';\n            }\n\n        } elseif (is_array($Harray) && !empty($Harray)) {\n\n            $odd = null;\n            $even = null;\n\n            if ($side === 'O') {\n                $odd = $Harray;\n            } elseif ($side === 'E') {\n                $even = $Harray;\n            } else {\n                $odd = Arrays::get($Harray, 'odd', null);\n                $even = Arrays::get($Harray, 'even', null);\n            }\n\n            $oddhtml = $this->_createHTMLheaderFooter($odd, 'H');\n            $evenhtml = $this->_createHTMLheaderFooter($even, 'H');\n        }\n\n        if ($side === 'E') {\n            $this->SetHTMLHeader($evenhtml, 'E', $write);\n        } elseif ($side === 'O') {\n            $this->SetHTMLHeader($oddhtml, 'O', $write);\n        } else {\n            $this->SetHTMLHeader($oddhtml, 'O', $write);\n            $this->SetHTMLHeader($evenhtml, 'E', $write);\n        }\n    }\n\n    function SetFooter($Farray = [], $side = '')\n    {\n        $oddhtml = '';\n        $evenhtml = '';\n\n        if (is_string($Farray)) {\n\n            if (strlen($Farray) == 0) {\n\n                $oddhtml = '';\n                $evenhtml = '';\n\n            } elseif (strpos($Farray, '|') !== false) {\n\n                $hdet = explode('|', $Farray);\n                $oddhtml = '<table width=\"100%\" style=\"border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';\n\n                if ($this->defaultfooterfontsize) {\n                    $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';\n                }\n\n                if ($this->defaultfooterfontstyle) {\n                    if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {\n                        $oddhtml .= ' font-weight: bold;';\n                    }\n                    if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {\n                        $oddhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultfooterline) {\n                    $oddhtml .= ' border-top: 0.1mm solid #000000;';\n                }\n\n                $oddhtml .= '\">';\n                $oddhtml .= '<tr>';\n                $oddhtml .= '<td width=\"33%\" style=\"padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; \">' . $hdet[0] . '</td>';\n                $oddhtml .= '<td width=\"33%\" style=\"padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; \">' . $hdet[1] . '</td>';\n                $oddhtml .= '<td width=\"33%\" style=\"padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; \">' . $hdet[2] . '</td>';\n                $oddhtml .= '</tr></table>';\n\n                $evenhtml = '<table width=\"100%\" style=\"border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';\n\n                if ($this->defaultfooterfontsize) {\n                    $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';\n                }\n\n                if ($this->defaultfooterfontstyle) {\n\n                    if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {\n                        $evenhtml .= ' font-weight: bold;';\n                    }\n\n                    if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {\n                        $evenhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultfooterline) {\n                    $evenhtml .= ' border-top: 0.1mm solid #000000;';\n                }\n\n                $evenhtml .= '\">';\n                $evenhtml .= '<tr>';\n                $evenhtml .= '<td width=\"33%\" style=\"padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; \">' . $hdet[2] . '</td>';\n                $evenhtml .= '<td width=\"33%\" style=\"padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; \">' . $hdet[1] . '</td>';\n                $evenhtml .= '<td width=\"33%\" style=\"padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; \">' . $hdet[0] . '</td>';\n                $evenhtml .= '</tr></table>';\n\n            } else {\n\n                $oddhtml = '<div style=\"margin: 0; color: #000000; ';\n\n                if ($this->defaultfooterfontsize) {\n                    $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';\n                }\n\n                if ($this->defaultfooterfontstyle) {\n\n                    if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {\n                        $oddhtml .= ' font-weight: bold;';\n                    }\n\n                    if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {\n                        $oddhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultfooterline) {\n                    $oddhtml .= ' border-top: 0.1mm solid #000000;';\n                }\n\n                $oddhtml .= 'text-align: right; \">' . $Farray . '</div>';\n\n                $evenhtml = '<div style=\"margin: 0; color: #000000; ';\n\n                if ($this->defaultfooterfontsize) {\n                    $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';\n                }\n\n                if ($this->defaultfooterfontstyle) {\n\n                    if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {\n                        $evenhtml .= ' font-weight: bold;';\n                    }\n\n                    if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {\n                        $evenhtml .= ' font-style: italic;';\n                    }\n                }\n\n                if ($this->defaultfooterline) {\n                    $evenhtml .= ' border-top: 0.1mm solid #000000;';\n                }\n\n                $evenhtml .= 'text-align: left; \">' . $Farray . '</div>';\n            }\n\n        } elseif (is_array($Farray)) {\n\n            $odd = null;\n            $even = null;\n\n            if ($side === 'O') {\n                $odd = $Farray;\n            } elseif ($side == 'E') {\n                $even = $Farray;\n            } else {\n                $odd = Arrays::get($Farray, 'odd', null);\n                $even = Arrays::get($Farray, 'even', null);\n            }\n\n            $oddhtml = $this->_createHTMLheaderFooter($odd, 'F');\n            $evenhtml = $this->_createHTMLheaderFooter($even, 'F');\n        }\n\n        if ($side === 'E') {\n            $this->SetHTMLFooter($evenhtml, 'E');\n        } elseif ($side === 'O') {\n            $this->SetHTMLFooter($oddhtml, 'O');\n        } else {\n            $this->SetHTMLFooter($oddhtml, 'O');\n            $this->SetHTMLFooter($evenhtml, 'E');\n        }\n    }\n\n    /* -- WATERMARK -- */\n\n    function SetWatermarkText($txt = '', $alpha = -1)\n    {\n        if ($alpha >= 0) {\n            $this->watermarkTextAlpha = $alpha;\n        }\n        $this->watermarkText = $txt;\n    }\n\n    function SetWatermarkImage($src, $alpha = -1, $size = 'D', $pos = 'F')\n    {\n        if ($alpha >= 0) {\n            $this->watermarkImageAlpha = $alpha;\n        }\n        $this->watermarkImage = $src;\n        $this->watermark_size = $size;\n        $this->watermark_pos = $pos;\n    }\n\n    /* -- END WATERMARK -- */\n\n    // Page footer\n    function Footer()\n    {\n        /* -- CSS-PAGE -- */\n        // PAGED MEDIA - CROP / CROSS MARKS from @PAGE\n        if ($this->show_marks == 'CROP' || $this->show_marks == 'CROPCROSS') {\n            // Show TICK MARKS\n            $this->SetLineWidth(0.1); // = 0.1 mm\n            $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            $l = $this->cropMarkLength;\n            $m = $this->cropMarkMargin; // Distance of crop mark from margin\n            $b = $this->nonPrintMargin; // Non-printable border at edge of paper sheet\n            $ax1 = $b;\n            $bx = $this->page_box['outer_width_LR'] - $m;\n            $ax = max($ax1, $bx - $l);\n            $cx1 = $this->w - $b;\n            $dx = $this->w - $this->page_box['outer_width_LR'] + $m;\n            $cx = min($cx1, $dx + $l);\n            $ay1 = $b;\n            $by = $this->page_box['outer_width_TB'] - $m;\n            $ay = max($ay1, $by - $l);\n            $cy1 = $this->h - $b;\n            $dy = $this->h - $this->page_box['outer_width_TB'] + $m;\n            $cy = min($cy1, $dy + $l);\n\n            $this->Line($ax, $this->page_box['outer_width_TB'], $bx, $this->page_box['outer_width_TB']);\n            $this->Line($cx, $this->page_box['outer_width_TB'], $dx, $this->page_box['outer_width_TB']);\n            $this->Line($ax, $this->h - $this->page_box['outer_width_TB'], $bx, $this->h - $this->page_box['outer_width_TB']);\n            $this->Line($cx, $this->h - $this->page_box['outer_width_TB'], $dx, $this->h - $this->page_box['outer_width_TB']);\n            $this->Line($this->page_box['outer_width_LR'], $ay, $this->page_box['outer_width_LR'], $by);\n            $this->Line($this->page_box['outer_width_LR'], $cy, $this->page_box['outer_width_LR'], $dy);\n            $this->Line($this->w - $this->page_box['outer_width_LR'], $ay, $this->w - $this->page_box['outer_width_LR'], $by);\n            $this->Line($this->w - $this->page_box['outer_width_LR'], $cy, $this->w - $this->page_box['outer_width_LR'], $dy);\n\n            if ($this->printers_info) {\n                $hd = date('Y-m-d H:i') . '  Page ' . $this->page . ' of {nb}';\n                $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                $this->SetFont('arial', '', 7.5, true, true);\n                $this->x = $this->page_box['outer_width_LR'] + 1.5;\n                $this->y = 1;\n                $this->Cell($headerpgwidth, $this->FontSize, $hd, 0, 0, 'L', 0, '', 0, 0, 0, 'M');\n                $this->SetFont($this->default_font, '', $this->original_default_font_size);\n            }\n        }\n        if ($this->show_marks == 'CROSS' || $this->show_marks == 'CROPCROSS') {\n            $this->SetLineWidth(0.1); // = 0.1 mm\n            $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            $l = 14 / 2; // longer length of the cross line (half)\n            $w = 6 / 2; // shorter width of the cross line (half)\n            $r = 1.2; // radius of circle\n            $m = $this->crossMarkMargin; // Distance of cross mark from margin\n            $x1 = $this->page_box['outer_width_LR'] - $m;\n            $x2 = $this->w - $this->page_box['outer_width_LR'] + $m;\n            $y1 = $this->page_box['outer_width_TB'] - $m;\n            $y2 = $this->h - $this->page_box['outer_width_TB'] + $m;\n            // Left\n            $this->Circle($x1, $this->h / 2, $r, 'S');\n            $this->Line($x1 - $w, $this->h / 2, $x1 + $w, $this->h / 2);\n            $this->Line($x1, $this->h / 2 - $l, $x1, $this->h / 2 + $l);\n            // Right\n            $this->Circle($x2, $this->h / 2, $r, 'S');\n            $this->Line($x2 - $w, $this->h / 2, $x2 + $w, $this->h / 2);\n            $this->Line($x2, $this->h / 2 - $l, $x2, $this->h / 2 + $l);\n            // Top\n            $this->Circle($this->w / 2, $y1, $r, 'S');\n            $this->Line($this->w / 2, $y1 - $w, $this->w / 2, $y1 + $w);\n            $this->Line($this->w / 2 - $l, $y1, $this->w / 2 + $l, $y1);\n            // Bottom\n            $this->Circle($this->w / 2, $y2, $r, 'S');\n            $this->Line($this->w / 2, $y2 - $w, $this->w / 2, $y2 + $w);\n            $this->Line($this->w / 2 - $l, $y2, $this->w / 2 + $l, $y2);\n        }\n\n        /* -- END CSS-PAGE -- */\n\n        // mPDF 6\n        // If @page set non-HTML headers/footers named, they were not read until later in the HTML code - so now set them\n        if ($this->page == 1) {\n            if ($this->firstPageBoxHeader) {\n                if (isset($this->pageHTMLheaders[$this->firstPageBoxHeader])) {\n                    $this->HTMLHeader = $this->pageHTMLheaders[$this->firstPageBoxHeader];\n                }\n                $this->Header();\n            }\n            if ($this->firstPageBoxFooter) {\n                if (isset($this->pageHTMLfooters[$this->firstPageBoxFooter])) {\n                    $this->HTMLFooter = $this->pageHTMLfooters[$this->firstPageBoxFooter];\n                }\n            }\n            $this->firstPageBoxHeader = '';\n            $this->firstPageBoxFooter = '';\n        }\n\n\n        if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLFooterE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLFooter) || (!$this->mirrorMargins && $this->HTMLFooter)) {\n            $this->writeHTMLFooters();\n        }\n\n        /* -- WATERMARK -- */\n        if (($this->watermarkText) && ($this->showWatermarkText)) {\n            $this->watermark($this->watermarkText, $this->watermarkAngle, 120, $this->watermarkTextAlpha); // Watermark text\n        }\n        if (($this->watermarkImage) && ($this->showWatermarkImage)) {\n            $this->watermarkImg($this->watermarkImage, $this->watermarkImageAlpha); // Watermark image\n        }\n        /* -- END WATERMARK -- */\n    }\n\n    /* -- HTML-CSS -- */\n\n    /**\n     * Write HTML code to the document\n     *\n     * Also used internally to parse HTML into buffers\n     *\n     * @param string $html\n     * @param int    $mode  Use HTMLParserMode constants. Controls what parts of the $html code is parsed.\n     * @param bool   $init  Clears and sets buffers to Top level block etc.\n     * @param bool   $close If false leaves buffers etc. in current state, so that it can continue a block etc.\n     */\n    function WriteHTML($html, $mode = HTMLParserMode::DEFAULT_MODE, $init = true, $close = true)\n    {\n        /* Check $html is an integer, float, string, boolean or class with __toString(), otherwise throw exception */\n        if (is_scalar($html) === false) {\n            if (!is_object($html) || ! method_exists($html, '__toString')) {\n                throw new \\Mpdf\\MpdfException('WriteHTML() requires $html be an integer, float, string, boolean or an object with the __toString() magic method.');\n            }\n        }\n\n        // Check the mode is valid\n        if (in_array($mode, HTMLParserMode::getAllModes(), true) === false) {\n            throw new \\Mpdf\\MpdfException('WriteHTML() requires $mode to be one of the modes defined in HTMLParserMode');\n        }\n\n        /* Cast $html as a string */\n        $html = (string) $html;\n\n        // @log Parsing CSS & Headers\n\n        if ($init) {\n            $this->headerbuffer = '';\n            $this->textbuffer = [];\n            $this->fixedPosBlockSave = [];\n        }\n        if ($mode === HTMLParserMode::HEADER_CSS) {\n            $html = '<style> ' . $html . ' </style>';\n        } // stylesheet only\n\n        if ($this->allow_charset_conversion) {\n            if ($mode === HTMLParserMode::DEFAULT_MODE) {\n                $this->ReadCharset($html);\n            }\n            if ($this->charset_in && $mode !== HTMLParserMode::HTML_HEADER_BUFFER) {\n                $success = iconv($this->charset_in, 'UTF-8//TRANSLIT', $html);\n                if ($success) {\n                    $html = $success;\n                }\n            }\n        }\n\n        $html = $this->purify_utf8($html, false);\n        if ($init) {\n            $this->blklvl = 0;\n            $this->lastblocklevelchange = 0;\n            $this->blk = [];\n            $this->initialiseBlock($this->blk[0]);\n            $this->blk[0]['width'] = & $this->pgwidth;\n            $this->blk[0]['inner_width'] = & $this->pgwidth;\n            $this->blk[0]['blockContext'] = $this->blockContext;\n        }\n\n        $zproperties = [];\n        if ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HEADER_CSS) {\n            $this->ReadMetaTags($html);\n\n            if (preg_match('/<base[^>]*href=[\"\\']([^\"\\'>]*)[\"\\']/i', $html, $m)) {\n                $this->SetBasePath($m[1]);\n            }\n            $html = $this->cssManager->ReadCSS($html);\n\n            if ($this->autoLangToFont && !$this->usingCoreFont && preg_match('/<html [^>]*lang=[\\'\\\"](.*?)[\\'\\\"]/ism', $html, $m)) {\n                $html_lang = $m[1];\n            }\n\n            if (preg_match('/<html [^>]*dir=[\\'\\\"]\\s*rtl\\s*[\\'\\\"]/ism', $html)) {\n                $zproperties['DIRECTION'] = 'rtl';\n            }\n\n            // allow in-line CSS for body tag to be parsed // Get <body> tag inline CSS\n            if (preg_match('/<body([^>]*)>(.*?)<\\/body>/ism', $html, $m) || preg_match('/<body([^>]*)>(.*)$/ism', $html, $m)) {\n                $html = $m[2];\n                // Changed to allow style=\"background: url('bg.jpg')\"\n                if (preg_match('/style=[\\\"](.*?)[\\\"]/ism', $m[1], $mm) || preg_match('/style=[\\'](.*?)[\\']/ism', $m[1], $mm)) {\n                    $zproperties = $this->cssManager->readInlineCSS($mm[1]);\n                }\n                if (preg_match('/dir=[\\'\\\"]\\s*rtl\\s*[\\'\\\"]/ism', $m[1])) {\n                    $zproperties['DIRECTION'] = 'rtl';\n                }\n                if (isset($html_lang) && $html_lang) {\n                    $zproperties['LANG'] = $html_lang;\n                }\n                if ($this->autoLangToFont && !$this->onlyCoreFonts && preg_match('/lang=[\\'\\\"](.*?)[\\'\\\"]/ism', $m[1], $mm)) {\n                    $zproperties['LANG'] = $mm[1];\n                }\n            }\n        }\n        $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');\n        if ($zproperties) {\n            $properties = $this->cssManager->array_merge_recursive_unique($properties, $zproperties);\n        }\n\n        if (isset($properties['DIRECTION']) && $properties['DIRECTION']) {\n            $this->cssManager->CSS['BODY']['DIRECTION'] = $properties['DIRECTION'];\n        }\n        if (!isset($this->cssManager->CSS['BODY']['DIRECTION'])) {\n            $this->cssManager->CSS['BODY']['DIRECTION'] = $this->directionality;\n        } else {\n            $this->SetDirectionality($this->cssManager->CSS['BODY']['DIRECTION']);\n        }\n\n        $this->setCSS($properties, '', 'BODY');\n\n        $this->blk[0]['InlineProperties'] = $this->saveInlineProperties();\n\n        if ($mode === HTMLParserMode::HEADER_CSS) {\n            return '';\n        }\n        if (!isset($this->cssManager->CSS['BODY'])) {\n            $this->cssManager->CSS['BODY'] = [];\n        }\n\n        /* -- BACKGROUNDS -- */\n        if (isset($properties['BACKGROUND-GRADIENT'])) {\n            $this->bodyBackgroundGradient = $properties['BACKGROUND-GRADIENT'];\n        }\n\n        if (isset($properties['BACKGROUND-IMAGE']) && $properties['BACKGROUND-IMAGE']) {\n            $ret = $this->SetBackground($properties, $this->pgwidth);\n            if ($ret) {\n                $this->bodyBackgroundImage = $ret;\n            }\n        }\n        /* -- END BACKGROUNDS -- */\n\n        /* -- CSS-PAGE -- */\n        // If page-box is set\n        if ($this->state == 0 && ((isset($this->cssManager->CSS['@PAGE']) && $this->cssManager->CSS['@PAGE']) || (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']))) { // mPDF 5.7.3\n            $this->page_box['current'] = '';\n            $this->page_box['using'] = true;\n            [$pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat] = $this->SetPagedMediaCSS('', false, 'O');\n            $this->DefOrientation = $this->CurOrientation = $pborientation;\n            $this->orig_lMargin = $this->DeflMargin = $pbmgl;\n            $this->orig_rMargin = $this->DefrMargin = $pbmgr;\n            $this->orig_tMargin = $this->tMargin = $pbmgt;\n            $this->orig_bMargin = $this->bMargin = $pbmgb;\n            $this->orig_hMargin = $this->margin_header = $pbmgh;\n            $this->orig_fMargin = $this->margin_footer = $pbmgf;\n            [$pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat] = $this->SetPagedMediaCSS('', true, 'O'); // first page\n            $this->show_marks = $marks;\n            if ($hname) {\n                $this->firstPageBoxHeader = $hname;\n            }\n            if ($fname) {\n                $this->firstPageBoxFooter = $fname;\n            }\n        }\n        /* -- END CSS-PAGE -- */\n\n        $parseonly = false;\n        $this->bufferoutput = false;\n        if ($mode == HTMLParserMode::HTML_PARSE_NO_WRITE) {\n            $parseonly = true;\n            // Close any open block tags\n            $arr = [];\n            $ai = 0;\n            for ($b = $this->blklvl; $b > 0; $b--) {\n                $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);\n            }\n            // Output any text left in buffer\n            if (count($this->textbuffer)) {\n                $this->printbuffer($this->textbuffer);\n            }\n            $this->textbuffer = [];\n        } elseif ($mode === HTMLParserMode::HTML_HEADER_BUFFER) {\n            // Close any open block tags\n            $arr = [];\n            $ai = 0;\n            for ($b = $this->blklvl; $b > 0; $b--) {\n                $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);\n            }\n            // Output any text left in buffer\n            if (count($this->textbuffer)) {\n                $this->printbuffer($this->textbuffer);\n            }\n            $this->bufferoutput = true;\n            $this->textbuffer = [];\n            $this->headerbuffer = '';\n            $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');\n            $this->setCSS($properties, '', 'BODY');\n        }\n\n        mb_internal_encoding('UTF-8');\n\n        $html = $this->AdjustHTML($html, $this->tabSpaces); // Try to make HTML look more like XHTML\n\n        if ($this->autoScriptToLang) {\n            $html = $this->markScriptToLang($html);\n        }\n\n        preg_match_all('/<htmlpageheader([^>]*)>(.*?)<\\/htmlpageheader>/si', $html, $h);\n        for ($i = 0; $i < count($h[1]); $i++) {\n            if (preg_match('/name=[\\'|\\\"](.*?)[\\'|\\\"]/', $h[1][$i], $n)) {\n                $this->pageHTMLheaders[$n[1]]['html'] = $h[2][$i];\n                $this->pageHTMLheaders[$n[1]]['h'] = $this->_getHtmlHeight($h[2][$i]);\n            }\n        }\n        preg_match_all('/<htmlpagefooter([^>]*)>(.*?)<\\/htmlpagefooter>/si', $html, $f);\n        for ($i = 0; $i < count($f[1]); $i++) {\n            if (preg_match('/name=[\\'|\\\"](.*?)[\\'|\\\"]/', $f[1][$i], $n)) {\n                $this->pageHTMLfooters[$n[1]]['html'] = $f[2][$i];\n                $this->pageHTMLfooters[$n[1]]['h'] = $this->_getHtmlHeight($f[2][$i]);\n            }\n        }\n\n        $html = preg_replace('/<htmlpageheader.*?<\\/htmlpageheader>/si', '', $html);\n        $html = preg_replace('/<htmlpagefooter.*?<\\/htmlpagefooter>/si', '', $html);\n\n        if ($this->state == 0 && ($mode === HTMLParserMode::DEFAULT_MODE || $mode === HTMLParserMode::HTML_BODY)) {\n            $this->AddPage($this->CurOrientation);\n        }\n\n\n        if (isset($hname) && preg_match('/^html_(.*)$/i', $hname, $n)) {\n            $this->SetHTMLHeader($this->pageHTMLheaders[$n[1]], 'O', true);\n        }\n        if (isset($fname) && preg_match('/^html_(.*)$/i', $fname, $n)) {\n            $this->SetHTMLFooter($this->pageHTMLfooters[$n[1]], 'O');\n        }\n\n\n\n        $html = str_replace('<?', '< ', $html); // Fix '<?XML' bug from HTML code generated by MS Word\n\n        $this->checkSIP = false;\n        $this->checkSMP = false;\n        $this->checkCJK = false;\n        if ($this->onlyCoreFonts) {\n            $html = $this->SubstituteChars($html);\n        } else {\n            if (preg_match(\"/([\" . $this->pregRTLchars . \"])/u\", $html)) {\n                $this->biDirectional = true;\n            } // *OTL*\n            if (preg_match(\"/([\\x{20000}-\\x{2FFFF}])/u\", $html)) {\n                $this->checkSIP = true;\n            }\n            if (preg_match(\"/([\\x{10000}-\\x{1FFFF}])/u\", $html)) {\n                $this->checkSMP = true;\n            }\n            /* -- CJK-FONTS -- */\n            if (preg_match(\"/([\" . $this->pregCJKchars . \"])/u\", $html)) {\n                $this->checkCJK = true;\n            }\n            /* -- END CJK-FONTS -- */\n        }\n\n        // Don't allow non-breaking spaces that are converted to substituted chars or will break anyway and mess up table width calc.\n        $html = str_replace('<tta>160</tta>', chr(32), $html);\n        $html = str_replace('</tta><tta>', '|', $html);\n        $html = str_replace('</tts><tts>', '|', $html);\n        $html = str_replace('</ttz><ttz>', '|', $html);\n\n        // Add new supported tags in the DisableTags function\n        $html = strip_tags($html, $this->enabledtags); // remove all unsupported tags, but the ones inside the 'enabledtags' string\n        // Explode the string in order to parse the HTML code\n        $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);\n        // ? more accurate regexp that allows e.g. <a name=\"Silly <name>\">\n        // if changing - also change in fn.SubstituteChars()\n        // $a = preg_split ('/<((?:[^<>]+(?:\"[^\"]*\"|\\'[^\\']*\\')?)+)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);\n\n        if ($this->mb_enc) {\n            mb_internal_encoding($this->mb_enc);\n        }\n        $pbc = 0;\n        $this->subPos = -1;\n        $cnt = count($a);\n        for ($i = 0; $i < $cnt; $i++) {\n            $e = $a[$i];\n            if ($i % 2 == 0) {\n                // TEXT\n                if ($this->blk[$this->blklvl]['hide']) {\n                    continue;\n                }\n                if ($this->inlineDisplayOff) {\n                    continue;\n                }\n                if ($this->inMeter) {\n                    continue;\n                }\n\n                if ($this->inFixedPosBlock) {\n                    $this->fixedPosBlock .= $e;\n                    continue;\n                } // *CSS-POSITION*\n                if (strlen($e) == 0) {\n                    continue;\n                }\n\n                if ($this->ignorefollowingspaces && !$this->ispre) {\n                    if (strlen(ltrim($e)) == 0) {\n                        continue;\n                    }\n                    if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && substr($e, 0, 1) == ' ') {\n                        $this->ignorefollowingspaces = false;\n                        $e = ltrim($e);\n                    }\n                }\n\n                $this->OTLdata = null;  // mPDF 5.7.1\n\n                $e = UtfString::strcode2utf($e);\n                $e = $this->lesser_entity_decode($e);\n\n                if ($this->usingCoreFont) {\n                    // If core font is selected in document which is not onlyCoreFonts - substitute with non-core font\n                    if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->subPos < $i && !$this->specialcontent) {\n                        $cnt += $this->SubstituteCharsNonCore($a, $i, $e);\n                    }\n                    // CONVERT ENCODING\n                    $e = mb_convert_encoding($e, $this->mb_enc, 'UTF-8');\n                    if ($this->textvar & TextVars::FT_UPPERCASE) {\n                        $e = mb_strtoupper($e, $this->mb_enc);\n                    } // mPDF 5.7.1\n                    elseif ($this->textvar & TextVars::FT_LOWERCASE) {\n                        $e = mb_strtolower($e, $this->mb_enc);\n                    } // mPDF 5.7.1\n                    elseif ($this->textvar & TextVars::FT_CAPITALIZE) {\n                        $e = mb_convert_case($e, MB_CASE_TITLE, \"UTF-8\");\n                    } // mPDF 5.7.1\n                } else {\n                    if ($this->checkSIP && $this->CurrentFont['sipext'] && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {\n                        $cnt += $this->SubstituteCharsSIP($a, $i, $e);\n                    }\n\n                    if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->CurrentFont['type'] != 'Type0' && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {\n                        $cnt += $this->SubstituteCharsMB($a, $i, $e);\n                    }\n\n                    if ($this->textvar & TextVars::FT_UPPERCASE) {\n                        $e = mb_strtoupper($e, $this->mb_enc);\n                    } elseif ($this->textvar & TextVars::FT_LOWERCASE) {\n                        $e = mb_strtolower($e, $this->mb_enc);\n                    } elseif ($this->textvar & TextVars::FT_CAPITALIZE) {\n                        $e = mb_convert_case($e, MB_CASE_TITLE, \"UTF-8\");\n                    }\n\n                    /* -- OTL -- */\n                    // Use OTL OpenType Table Layout - GSUB & GPOS\n                    if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL'] && (!$this->specialcontent || !$this->useActiveForms)) {\n                        if (!$this->otl) {\n                            $this->otl = new Otl($this, $this->fontCache);\n                        }\n                        $e = $this->otl->applyOTL($e, $this->CurrentFont['useOTL']);\n                        $this->OTLdata = $this->otl->OTLdata;\n                        $this->otl->removeChar($e, $this->OTLdata, \"\\xef\\xbb\\xbf\"); // Remove ZWNBSP (also Byte order mark FEFF)\n                    } /* -- END OTL -- */\n                    else {\n                        // removes U+200E/U+200F LTR and RTL mark and U+200C/U+200D Zero-width Joiner and Non-joiner\n                        $e = preg_replace(\"/[\\xe2\\x80\\x8c\\xe2\\x80\\x8d\\xe2\\x80\\x8e\\xe2\\x80\\x8f]/u\", '', $e);\n                        $e = preg_replace(\"/[\\xef\\xbb\\xbf]/u\", '', $e); // Remove ZWNBSP (also Byte order mark FEFF)\n                    }\n                }\n\n                if (($this->tts) || ($this->ttz) || ($this->tta)) {\n                    $es = explode('|', $e);\n                    $e = '';\n                    foreach ($es as $val) {\n                        $e .= chr($val);\n                    }\n                }\n\n                //  FORM ELEMENTS\n                if ($this->specialcontent) {\n                    /* -- FORMS -- */\n                    // SELECT tag (form element)\n                    if ($this->specialcontent == \"type=select\") {\n                        $e = ltrim($e);\n                        if (!empty($this->OTLdata)) {\n                            $this->otl->trimOTLdata($this->OTLdata, true, false);\n                        } // *OTL*\n                        $stringwidth = $this->GetStringWidth($e);\n                        if (!isset($this->selectoption['MAXWIDTH']) || $stringwidth > $this->selectoption['MAXWIDTH']) {\n                            $this->selectoption['MAXWIDTH'] = $stringwidth;\n                        }\n                        if (!isset($this->selectoption['SELECTED']) || $this->selectoption['SELECTED'] == '') {\n                            $this->selectoption['SELECTED'] = $e;\n                            if (!empty($this->OTLdata)) {\n                                $this->selectoption['SELECTED-OTLDATA'] = $this->OTLdata;\n                            } // *OTL*\n                        }\n                        // Active Forms\n                        if (isset($this->selectoption['ACTIVE']) && $this->selectoption['ACTIVE']) {\n                            $this->selectoption['ITEMS'][] = ['exportValue' => $this->selectoption['currentVAL'], 'content' => $e, 'selected' => $this->selectoption['currentSEL']];\n                        }\n                        $this->OTLdata = [];\n                    } // TEXTAREA\n                    else {\n                        $objattr = unserialize($this->specialcontent);\n                        $objattr['text'] = $e;\n                        $objattr['OTLdata'] = $this->OTLdata;\n                        $this->OTLdata = [];\n                        $te = \"\\xbb\\xa4\\xactype=textarea,objattr=\" . serialize($objattr) . \"\\xbb\\xa4\\xac\";\n                        if ($this->tdbegin) {\n                            $this->_saveCellTextBuffer($te, $this->HREF);\n                        } else {\n                            $this->_saveTextBuffer($te, $this->HREF);\n                        }\n                    }\n                    /* -- END FORMS -- */\n                } // TABLE\n                elseif ($this->tableLevel) {\n                    /* -- TABLES -- */\n                    if ($this->tdbegin) {\n                        if (($this->ignorefollowingspaces) && !$this->ispre) {\n                            $e = ltrim($e);\n                            if (!empty($this->OTLdata)) {\n                                $this->otl->trimOTLdata($this->OTLdata, true, false);\n                            } // *OTL*\n                        }\n                        if ($e || $e === '0') {\n                            if ($this->blockjustfinished && $this->cell[$this->row][$this->col]['s'] > 0) {\n                                $this->_saveCellTextBuffer(\"\\n\");\n                                if (!isset($this->cell[$this->row][$this->col]['maxs'])) {\n                                    $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];\n                                } elseif ($this->cell[$this->row][$this->col]['maxs'] < $this->cell[$this->row][$this->col]['s']) {\n                                    $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];\n                                }\n                                $this->cell[$this->row][$this->col]['s'] = 0; // reset\n                            }\n                            $this->blockjustfinished = false;\n\n                            if (!isset($this->cell[$this->row][$this->col]['R']) || !$this->cell[$this->row][$this->col]['R']) {\n                                if (isset($this->cell[$this->row][$this->col]['s'])) {\n                                    $this->cell[$this->row][$this->col]['s'] += $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);\n                                } else {\n                                    $this->cell[$this->row][$this->col]['s'] = $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);\n                                }\n                                if (!empty($this->spanborddet)) {\n                                    $this->cell[$this->row][$this->col]['s'] += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0) + (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);\n                                }\n                            }\n                            $this->_saveCellTextBuffer($e, $this->HREF);\n                            if (substr($this->cell[$this->row][$this->col]['a'], 0, 1) == 'D') {\n                                $dp = $this->decimal_align[substr($this->cell[$this->row][$this->col]['a'], 0, 2)];\n                                $s = preg_split('/' . preg_quote($dp, '/') . '/', $e, 2);  // ? needs to be /u if not core\n                                $s0 = $this->GetStringWidth($s[0], false);\n                                if (isset($s[1]) && $s[1]) {\n                                    $s1 = $this->GetStringWidth(($s[1] . $dp), false);\n                                } else {\n                                    $s1 = 0;\n                                }\n                                if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'])) {\n                                    $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = $s0;\n                                } else {\n                                    $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = max($s0, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0']);\n                                }\n                                if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'])) {\n                                    $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = $s1;\n                                } else {\n                                    $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = max($s1, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1']);\n                                }\n                            }\n\n                            $this->nestedtablejustfinished = false;\n                            $this->linebreakjustfinished = false;\n                        }\n                    }\n                    /* -- END TABLES -- */\n                } // ALL ELSE\n                else {\n                    if ($this->ignorefollowingspaces && !$this->ispre) {\n                        $e = ltrim($e);\n                        if (!empty($this->OTLdata)) {\n                            $this->otl->trimOTLdata($this->OTLdata, true, false);\n                        } // *OTL*\n                    }\n                    if ($e || $e === '0') {\n                        $this->_saveTextBuffer($e, $this->HREF);\n                    }\n                }\n                if ($e || $e === '0') {\n                    $this->ignorefollowingspaces = false; // mPDF 6\n                }\n                if (substr($e, -1, 1) == ' ' && !$this->ispre && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {\n                    $this->ignorefollowingspaces = true;\n                }\n            } else { // TAG **\n                if (isset($e[0]) && $e[0] == '/') {\n                    $endtag = trim(strtoupper(substr($e, 1)));\n\n                    /* -- CSS-POSITION -- */\n                    // mPDF 6\n                    if ($this->inFixedPosBlock) {\n                        if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {\n                            $this->fixedPosBlockDepth--;\n                        }\n                        if ($this->fixedPosBlockDepth == 0) {\n                            $this->fixedPosBlockSave[] = [$this->fixedPosBlock, $this->fixedPosBlockBBox, $this->page];\n                            $this->fixedPosBlock = '';\n                            $this->inFixedPosBlock = false;\n                            continue;\n                        }\n                        $this->fixedPosBlock .= '<' . $e . '>';\n                        continue;\n                    }\n                    /* -- END CSS-POSITION -- */\n\n                    // mPDF 6\n                    // Correct for tags where HTML5 specifies optional end tags (see also OpenTag() )\n                    if ($this->allow_html_optional_endtags && !$parseonly) {\n                        if (isset($this->blk[$this->blklvl]['tag'])) {\n                            $closed = false;\n                            // li end tag may be omitted if there is no more content in the parent element\n                            if (!$closed && $this->blk[$this->blklvl]['tag'] == 'LI' && $endtag != 'LI' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {\n                                $this->tag->CloseTag('LI', $a, $i);\n                                $closed = true;\n                            }\n                            // dd end tag may be omitted if there is no more content in the parent element\n                            if (!$closed && $this->blk[$this->blklvl]['tag'] == 'DD' && $endtag != 'DD' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {\n                                $this->tag->CloseTag('DD', $a, $i);\n                                $closed = true;\n                            }\n                            // p end tag may be omitted if there is no more content in the parent element and the parent element is not an A element [??????]\n                            if (!$closed && $this->blk[$this->blklvl]['tag'] == 'P' && $endtag != 'P' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {\n                                $this->tag->CloseTag('P', $a, $i);\n                                $closed = true;\n                            }\n                            // option end tag may be omitted if there is no more content in the parent element\n                            if (!$closed && $this->blk[$this->blklvl]['tag'] == 'OPTION' && $endtag != 'OPTION' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {\n                                $this->tag->CloseTag('OPTION', $a, $i);\n                                $closed = true;\n                            }\n                        }\n                        /* -- TABLES -- */\n                        // Check for Table tags where HTML specifies optional end tags,\n                        if ($endtag == 'TABLE') {\n                            if ($this->lastoptionaltag == 'THEAD' || $this->lastoptionaltag == 'TBODY' || $this->lastoptionaltag == 'TFOOT') {\n                                $this->tag->CloseTag($this->lastoptionaltag, $a, $i);\n                            }\n                            if ($this->lastoptionaltag == 'TR') {\n                                $this->tag->CloseTag('TR', $a, $i);\n                            }\n                            if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {\n                                $this->tag->CloseTag($this->lastoptionaltag, $a, $i);\n                                $this->tag->CloseTag('TR', $a, $i);\n                            }\n                        }\n                        if ($endtag == 'THEAD' || $endtag == 'TBODY' || $endtag == 'TFOOT') {\n                            if ($this->lastoptionaltag == 'TR') {\n                                $this->tag->CloseTag('TR', $a, $i);\n                            }\n                            if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {\n                                $this->tag->CloseTag($this->lastoptionaltag, $a, $i);\n                                $this->tag->CloseTag('TR', $a, $i);\n                            }\n                        }\n                        if ($endtag == 'TR') {\n                            if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {\n                                $this->tag->CloseTag($this->lastoptionaltag, $a, $i);\n                            }\n                        }\n                        /* -- END TABLES -- */\n                    }\n\n\n                    // mPDF 6\n                    if ($this->blk[$this->blklvl]['hide']) {\n                        if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {\n                            unset($this->blk[$this->blklvl]);\n                            $this->blklvl--;\n                        }\n                        continue;\n                    }\n\n                    // mPDF 6\n                    $this->tag->CloseTag($endtag, $a, $i); // mPDF 6\n                } else { // OPENING TAG\n                    if ($this->blk[$this->blklvl]['hide']) {\n                        if (strpos($e, ' ')) {\n                            $te = strtoupper(substr($e, 0, strpos($e, ' ')));\n                        } else {\n                            $te = strtoupper($e);\n                        }\n                        // mPDF 6\n                        if ($te == 'THEAD' || $te == 'TBODY' || $te == 'TFOOT' || $te == 'TR' || $te == 'TD' || $te == 'TH') {\n                            $this->lastoptionaltag = $te;\n                        }\n                        if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {\n                            $this->blklvl++;\n                            $this->blk[$this->blklvl]['hide'] = true;\n                            $this->blk[$this->blklvl]['tag'] = $te; // mPDF 6\n                        }\n                        continue;\n                    }\n\n                    /* -- CSS-POSITION -- */\n                    if ($this->inFixedPosBlock) {\n                        if (strpos($e, ' ')) {\n                            $te = strtoupper(substr($e, 0, strpos($e, ' ')));\n                        } else {\n                            $te = strtoupper($e);\n                        }\n                        $this->fixedPosBlock .= '<' . $e . '>';\n                        if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {\n                            $this->fixedPosBlockDepth++;\n                        }\n                        continue;\n                    }\n                    /* -- END CSS-POSITION -- */\n                    $regexp = '|=\\'(.*?)\\'|s'; // eliminate single quotes, if any\n                    $e = preg_replace($regexp, \"=\\\"\\$1\\\"\", $e);\n                    // changes anykey=anyvalue to anykey=\"anyvalue\" (only do this inside [some] tags)\n                    if (substr($e, 0, 10) != 'pageheader' && substr($e, 0, 10) != 'pagefooter' && substr($e, 0, 12) != 'tocpagebreak' && substr($e, 0, 10) != 'indexentry' && substr($e, 0, 8) != 'tocentry') { // mPDF 6  (ZZZ99H)\n                        $regexp = '| (\\\\w+?)=([^\\\\s>\"]+)|si';\n                        $e = preg_replace($regexp, \" \\$1=\\\"\\$2\\\"\", $e);\n                    }\n\n                    $e = preg_replace('/ (\\\\S+?)\\s*=\\s*\"/i', \" \\\\1=\\\"\", $e);\n\n                    // Fix path values, if needed\n                    $orig_srcpath = '';\n                    if ((stristr($e, \"href=\") !== false) or ( stristr($e, \"src=\") !== false)) {\n                        $regexp = '/ (href|src)\\s*=\\s*\"(.*?)\"/i';\n                        preg_match($regexp, $e, $auxiliararray);\n                        if (isset($auxiliararray[2])) {\n                            $path = $auxiliararray[2];\n                        } else {\n                            $path = '';\n                        }\n                        if (trim($path) != '' && !(stristr($e, \"src=\") !== false && substr($path, 0, 4) == 'var:') && substr($path, 0, 1) != '@') {\n                            $path = htmlspecialchars_decode($path); // mPDF 5.7.4 URLs\n                            $orig_srcpath = $path;\n                            $this->GetFullPath($path);\n                            $regexp = '/ (href|src)=\"(.*?)\"/i';\n                            $e = preg_replace($regexp, ' \\\\1=\"' . $path . '\"', $e);\n                        }\n                    }//END of Fix path values\n                    // Extract attributes\n                    $contents = [];\n                    $contents1 = [];\n                    $contents2 = [];\n                    // Changed to allow style=\"background: url('bg.jpg')\"\n                    // Changed to improve performance; maximum length of \\S (attribute) = 16\n                    // Increase allowed attribute name to 32 - cutting off \"toc-even-header-name\" etc.\n                    preg_match_all('/\\\\S{1,32}=[\"][^\"]*[\"]/', $e, $contents1);\n                    preg_match_all('/\\\\S{1,32}=[\\'][^\\']*[\\']/i', $e, $contents2);\n\n                    $contents = array_merge($contents1, $contents2);\n                    preg_match('/\\\\S+/', $e, $a2);\n                    $tag = (isset($a2[0]) ? strtoupper($a2[0]) : '');\n                    $attr = [];\n                    if ($orig_srcpath) {\n                        $attr['ORIG_SRC'] = $orig_srcpath;\n                    }\n                    if (!empty($contents)) {\n                        foreach ($contents[0] as $v) {\n                            // Changed to allow style=\"background: url('bg.jpg')\"\n                            if (preg_match('/^([^=]*)=[\"]?([^\"]*)[\"]?$/', $v, $a3) || preg_match('/^([^=]*)=[\\']?([^\\']*)[\\']?$/', $v, $a3)) {\n                                if (strtoupper($a3[1]) == 'ID' || strtoupper($a3[1]) == 'CLASS') { // 4.2.013 Omits STYLE\n                                    $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));\n                                } // includes header-style-right etc. used for <pageheader>\n                                elseif (preg_match('/^(HEADER|FOOTER)-STYLE/i', $a3[1])) {\n                                    $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));\n                                } else {\n                                    $attr[strtoupper($a3[1])] = trim($a3[2]);\n                                }\n                            }\n                        }\n                    }\n                    $this->tag->OpenTag($tag, $attr, $a, $i); // mPDF 6\n                    /* -- CSS-POSITION -- */\n                    if ($this->inFixedPosBlock) {\n                        $this->fixedPosBlockBBox = [$tag, $attr, $this->x, $this->y];\n                        $this->fixedPosBlock = '';\n                        $this->fixedPosBlockDepth = 1;\n                    }\n                    /* -- END CSS-POSITION -- */\n                    if (preg_match('/\\/$/', $e)) {\n                        $this->tag->CloseTag($tag, $a, $i);\n                    }\n                }\n            } // end TAG\n        } // end of\tforeach($a as $i=>$e)\n\n        if ($close) {\n            // Close any open block tags\n            for ($b = $this->blklvl; $b > 0; $b--) {\n                $this->tag->CloseTag($this->blk[$b]['tag'], $a, $i);\n            }\n\n            // Output any text left in buffer\n            if (count($this->textbuffer) && !$parseonly) {\n                $this->printbuffer($this->textbuffer);\n            }\n            if (!$parseonly) {\n                $this->textbuffer = [];\n            }\n\n            /* -- CSS-FLOAT -- */\n            // If ended with a float, need to move to end page\n            $currpos = $this->page * 1000 + $this->y;\n            if (isset($this->blk[$this->blklvl]['float_endpos']) && $this->blk[$this->blklvl]['float_endpos'] > $currpos) {\n                $old_page = $this->page;\n                $new_page = intval($this->blk[$this->blklvl]['float_endpos'] / 1000);\n                if ($old_page != $new_page) {\n                    $s = $this->PrintPageBackgrounds();\n                    // Writes after the marker so not overwritten later by page background etc.\n                    $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\\\1' . \"\\n\" . $s . \"\\n\", $this->pages[$this->page]);\n                    $this->pageBackgrounds = [];\n                    $this->page = $new_page;\n                    $this->ResetMargins();\n                    $this->Reset();\n                    $this->pageoutput[$this->page] = [];\n                }\n                $this->y = (($this->blk[$this->blklvl]['float_endpos'] * 1000) % 1000000) / 1000; // mod changes operands to integers before processing\n            }\n            /* -- END CSS-FLOAT -- */\n\n            /* -- CSS-IMAGE-FLOAT -- */\n            $this->printfloatbuffer();\n            /* -- END CSS-IMAGE-FLOAT -- */\n\n            // Create Internal Links, if needed\n            if (!empty($this->internallink)) {\n\n                foreach ($this->internallink as $k => $v) {\n\n                    if (strpos($k, \"#\") !== false) {\n                        continue;\n                    }\n\n                    if (!is_array($v)) {\n                        continue;\n                    }\n\n                    $ypos = $v['Y'];\n                    $pagenum = $v['PAGE'];\n                    $sharp = \"#\";\n\n                    while (array_key_exists($sharp . $k, $this->internallink)) {\n                        $internallink = $this->internallink[$sharp . $k];\n                        $this->SetLink($internallink, $ypos, $pagenum);\n                        $sharp .= \"#\";\n                    }\n                }\n            }\n\n            $this->bufferoutput = false;\n\n            /* -- CSS-POSITION -- */\n            if (count($this->fixedPosBlockSave)) {\n                foreach ($this->fixedPosBlockSave as $fpbs) {\n                    $old_page = $this->page;\n                    $this->page = $fpbs[2];\n                    $this->WriteFixedPosHTML($fpbs[0], 0, 0, 100, 100, 'auto', $fpbs[1]);  // 0,0,10,10 are overwritten by bbox\n                    $this->page = $old_page;\n                }\n                $this->fixedPosBlockSave = [];\n            }\n            /* -- END CSS-POSITION -- */\n        }\n    }\n\n    /* -- CSS-POSITION -- */\n\n    function WriteFixedPosHTML($html, $x, $y, $w, $h, $overflow = 'visible', $bounding = [])\n    {\n        // $overflow can be 'hidden', 'visible' or 'auto' - 'auto' causes autofit to size\n        // Annotations disabled - enabled in mPDF 5.0\n        // Links do work\n        // Will always go on current page (or start Page 1 if required)\n        // Probably INCOMPATIBLE WITH keep with table, columns etc.\n        // Called externally or interally via <div style=\"position: [fixed|absolute]\">\n        // When used internally, $x $y $w $h and $overflow are all overridden by $bounding\n\n        $overflow = strtolower($overflow);\n        if ($this->state == 0) {\n            $this->AddPage($this->CurOrientation);\n        }\n        $save_y = $this->y;\n        $save_x = $this->x;\n        $this->fullImageHeight = $this->h;\n        $save_cols = false;\n        /* -- COLUMNS -- */\n        if ($this->ColActive) {\n            $save_cols = true;\n            $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off\n            $this->SetColumns(0);\n        }\n        /* -- END COLUMNS -- */\n        $save_annots = $this->title2annots; // *ANNOTATIONS*\n        $this->writingHTMLheader = true; // a FIX to stop pagebreaks etc.\n        $this->writingHTMLfooter = true;\n        $this->InFooter = true; // suppresses autopagebreaks\n        $save_bgs = $this->pageBackgrounds;\n        $checkinnerhtml = preg_replace('/\\s/', '', $html);\n        $rotate = 0;\n\n        if ($w > $this->w) {\n            $x = 0;\n            $w = $this->w;\n        }\n        if ($h > $this->h) {\n            $y = 0;\n            $h = $this->h;\n        }\n        if ($x > $this->w) {\n            $x = $this->w - $w;\n        }\n        if ($y > $this->h) {\n            $y = $this->h - $h;\n        }\n\n        if (!empty($bounding)) {\n            // $cont_ containing block = full physical page (position: absolute) or page inside margins (position: fixed)\n            // $bbox_ Bounding box is the <div> which is positioned absolutely/fixed\n            // top/left/right/bottom/width/height/background*/border*/padding*/margin* are taken from bounding\n            // font*[family/size/style/weight]/line-height/text*[align/decoration/transform/indent]/color are transferred to $inner\n            // as an enclosing <div> (after having checked ID/CLASS)\n            // $x, $y, $w, $h are inside of $bbox_ = containing box for $inner_\n            // $inner_ InnerHTML is the contents of that block to be output\n            $tag = $bounding[0];\n            $attr = $bounding[1];\n            $orig_x0 = $bounding[2];\n            $orig_y0 = $bounding[3];\n\n            // As in WriteHTML() initialising\n            $this->blklvl = 0;\n            $this->lastblocklevelchange = 0;\n            $this->blk = [];\n            $this->initialiseBlock($this->blk[0]);\n\n            $this->blk[0]['width'] = & $this->pgwidth;\n            $this->blk[0]['inner_width'] = & $this->pgwidth;\n\n            $this->blk[0]['blockContext'] = $this->blockContext;\n\n            $properties = $this->cssManager->MergeCSS('BLOCK', 'BODY', '');\n            $this->setCSS($properties, '', 'BODY');\n            $this->blklvl = 1;\n            $this->initialiseBlock($this->blk[1]);\n            $this->blk[1]['tag'] = $tag;\n            $this->blk[1]['attr'] = $attr;\n            $this->Reset();\n            $p = $this->cssManager->MergeCSS('BLOCK', $tag, $attr);\n            if (isset($p['ROTATE']) && ($p['ROTATE'] == 90 || $p['ROTATE'] == -90 || $p['ROTATE'] == 180)) {\n                $rotate = $p['ROTATE'];\n            } // mPDF 6\n            if (isset($p['OVERFLOW'])) {\n                $overflow = strtolower($p['OVERFLOW']);\n            }\n            if (strtolower($p['POSITION']) == 'fixed') {\n                $cont_w = $this->pgwidth; // $this->blk[0]['inner_width'];\n                $cont_h = $this->h - $this->tMargin - $this->bMargin;\n                $cont_x = $this->lMargin;\n                $cont_y = $this->tMargin;\n            } else {\n                $cont_w = $this->w; // ABSOLUTE;\n                $cont_h = $this->h;\n                $cont_x = 0;\n                $cont_y = 0;\n            }\n\n            // Pass on in-line properties to the innerhtml\n            $css = '';\n            if (isset($p['TEXT-ALIGN'])) {\n                $css .= 'text-align: ' . strtolower($p['TEXT-ALIGN']) . '; ';\n            }\n            if (isset($p['TEXT-TRANSFORM'])) {\n                $css .= 'text-transform: ' . strtolower($p['TEXT-TRANSFORM']) . '; ';\n            }\n            if (isset($p['TEXT-INDENT'])) {\n                $css .= 'text-indent: ' . strtolower($p['TEXT-INDENT']) . '; ';\n            }\n            if (isset($p['TEXT-DECORATION'])) {\n                $css .= 'text-decoration: ' . strtolower($p['TEXT-DECORATION']) . '; ';\n            }\n            if (isset($p['FONT-FAMILY'])) {\n                $css .= 'font-family: ' . strtolower($p['FONT-FAMILY']) . '; ';\n            }\n            if (isset($p['FONT-STYLE'])) {\n                $css .= 'font-style: ' . strtolower($p['FONT-STYLE']) . '; ';\n            }\n            if (isset($p['FONT-WEIGHT'])) {\n                $css .= 'font-weight: ' . strtolower($p['FONT-WEIGHT']) . '; ';\n            }\n            if (isset($p['FONT-SIZE'])) {\n                $css .= 'font-size: ' . strtolower($p['FONT-SIZE']) . '; ';\n            }\n            if (isset($p['LINE-HEIGHT'])) {\n                $css .= 'line-height: ' . strtolower($p['LINE-HEIGHT']) . '; ';\n            }\n            if (isset($p['TEXT-SHADOW'])) {\n                $css .= 'text-shadow: ' . strtolower($p['TEXT-SHADOW']) . '; ';\n            }\n            if (isset($p['LETTER-SPACING'])) {\n                $css .= 'letter-spacing: ' . strtolower($p['LETTER-SPACING']) . '; ';\n            }\n            // mPDF 6\n            if (isset($p['FONT-VARIANT-POSITION'])) {\n                $css .= 'font-variant-position: ' . strtolower($p['FONT-VARIANT-POSITION']) . '; ';\n            }\n            if (isset($p['FONT-VARIANT-CAPS'])) {\n                $css .= 'font-variant-caps: ' . strtolower($p['FONT-VARIANT-CAPS']) . '; ';\n            }\n            if (isset($p['FONT-VARIANT-LIGATURES'])) {\n                $css .= 'font-variant-ligatures: ' . strtolower($p['FONT-VARIANT-LIGATURES']) . '; ';\n            }\n            if (isset($p['FONT-VARIANT-NUMERIC'])) {\n                $css .= 'font-variant-numeric: ' . strtolower($p['FONT-VARIANT-NUMERIC']) . '; ';\n            }\n            if (isset($p['FONT-VARIANT-ALTERNATES'])) {\n                $css .= 'font-variant-alternates: ' . strtolower($p['FONT-VARIANT-ALTERNATES']) . '; ';\n            }\n            if (isset($p['FONT-FEATURE-SETTINGS'])) {\n                $css .= 'font-feature-settings: ' . strtolower($p['FONT-FEATURE-SETTINGS']) . '; ';\n            }\n            if (isset($p['FONT-LANGUAGE-OVERRIDE'])) {\n                $css .= 'font-language-override: ' . strtolower($p['FONT-LANGUAGE-OVERRIDE']) . '; ';\n            }\n            if (isset($p['FONT-KERNING'])) {\n                $css .= 'font-kerning: ' . strtolower($p['FONT-KERNING']) . '; ';\n            }\n\n            if (isset($p['COLOR'])) {\n                $css .= 'color: ' . strtolower($p['COLOR']) . '; ';\n            }\n            if (isset($p['Z-INDEX'])) {\n                $css .= 'z-index: ' . $p['Z-INDEX'] . '; ';\n            }\n            if ($css) {\n                $html = '<div style=\"' . $css . '\">' . $html . '</div>';\n            }\n            // Copy over (only) the properties to set for border and background\n            $pb = [];\n            $pb['MARGIN-TOP'] = (isset($p['MARGIN-TOP']) ? $p['MARGIN-TOP'] : '');\n            $pb['MARGIN-RIGHT'] = (isset($p['MARGIN-RIGHT']) ? $p['MARGIN-RIGHT'] : '');\n            $pb['MARGIN-BOTTOM'] = (isset($p['MARGIN-BOTTOM']) ? $p['MARGIN-BOTTOM'] : '');\n            $pb['MARGIN-LEFT'] = (isset($p['MARGIN-LEFT']) ? $p['MARGIN-LEFT'] : '');\n            $pb['PADDING-TOP'] = (isset($p['PADDING-TOP']) ? $p['PADDING-TOP'] : '');\n            $pb['PADDING-RIGHT'] = (isset($p['PADDING-RIGHT']) ? $p['PADDING-RIGHT'] : '');\n            $pb['PADDING-BOTTOM'] = (isset($p['PADDING-BOTTOM']) ? $p['PADDING-BOTTOM'] : '');\n            $pb['PADDING-LEFT'] = (isset($p['PADDING-LEFT']) ? $p['PADDING-LEFT'] : '');\n            $pb['BORDER-TOP'] = (isset($p['BORDER-TOP']) ? $p['BORDER-TOP'] : '');\n            $pb['BORDER-RIGHT'] = (isset($p['BORDER-RIGHT']) ? $p['BORDER-RIGHT'] : '');\n            $pb['BORDER-BOTTOM'] = (isset($p['BORDER-BOTTOM']) ? $p['BORDER-BOTTOM'] : '');\n            $pb['BORDER-LEFT'] = (isset($p['BORDER-LEFT']) ? $p['BORDER-LEFT'] : '');\n            if (isset($p['BORDER-TOP-LEFT-RADIUS-H'])) {\n                $pb['BORDER-TOP-LEFT-RADIUS-H'] = $p['BORDER-TOP-LEFT-RADIUS-H'];\n            }\n            if (isset($p['BORDER-TOP-LEFT-RADIUS-V'])) {\n                $pb['BORDER-TOP-LEFT-RADIUS-V'] = $p['BORDER-TOP-LEFT-RADIUS-V'];\n            }\n            if (isset($p['BORDER-TOP-RIGHT-RADIUS-H'])) {\n                $pb['BORDER-TOP-RIGHT-RADIUS-H'] = $p['BORDER-TOP-RIGHT-RADIUS-H'];\n            }\n            if (isset($p['BORDER-TOP-RIGHT-RADIUS-V'])) {\n                $pb['BORDER-TOP-RIGHT-RADIUS-V'] = $p['BORDER-TOP-RIGHT-RADIUS-V'];\n            }\n            if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-H'])) {\n                $pb['BORDER-BOTTOM-LEFT-RADIUS-H'] = $p['BORDER-BOTTOM-LEFT-RADIUS-H'];\n            }\n            if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-V'])) {\n                $pb['BORDER-BOTTOM-LEFT-RADIUS-V'] = $p['BORDER-BOTTOM-LEFT-RADIUS-V'];\n            }\n            if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-H'])) {\n                $pb['BORDER-BOTTOM-RIGHT-RADIUS-H'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-H'];\n            }\n            if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-V'])) {\n                $pb['BORDER-BOTTOM-RIGHT-RADIUS-V'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-V'];\n            }\n            if (isset($p['BACKGROUND-COLOR'])) {\n                $pb['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];\n            }\n            if (isset($p['BOX-SHADOW'])) {\n                $pb['BOX-SHADOW'] = $p['BOX-SHADOW'];\n            }\n            /* -- BACKGROUNDS -- */\n            if (isset($p['BACKGROUND-IMAGE'])) {\n                $pb['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];\n            }\n            if (isset($p['BACKGROUND-IMAGE-RESIZE'])) {\n                $pb['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];\n            }\n            if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {\n                $pb['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];\n            }\n            if (isset($p['BACKGROUND-REPEAT'])) {\n                $pb['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];\n            }\n            if (isset($p['BACKGROUND-POSITION'])) {\n                $pb['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];\n            }\n            if (isset($p['BACKGROUND-GRADIENT'])) {\n                $pb['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];\n            }\n            if (isset($p['BACKGROUND-SIZE'])) {\n                $pb['BACKGROUND-SIZE'] = $p['BACKGROUND-SIZE'];\n            }\n            if (isset($p['BACKGROUND-ORIGIN'])) {\n                $pb['BACKGROUND-ORIGIN'] = $p['BACKGROUND-ORIGIN'];\n            }\n            if (isset($p['BACKGROUND-CLIP'])) {\n                $pb['BACKGROUND-CLIP'] = $p['BACKGROUND-CLIP'];\n            }\n\n            /* -- END BACKGROUNDS -- */\n\n            $this->setCSS($pb, 'BLOCK', $tag);\n\n            // ================================================================\n            $bbox_br = $this->blk[1]['border_right']['w'];\n            $bbox_bl = $this->blk[1]['border_left']['w'];\n            $bbox_bt = $this->blk[1]['border_top']['w'];\n            $bbox_bb = $this->blk[1]['border_bottom']['w'];\n            $bbox_pr = $this->blk[1]['padding_right'];\n            $bbox_pl = $this->blk[1]['padding_left'];\n            $bbox_pt = $this->blk[1]['padding_top'];\n            $bbox_pb = $this->blk[1]['padding_bottom'];\n            $bbox_mr = $this->blk[1]['margin_right'];\n            if (isset($p['MARGIN-RIGHT']) && strtolower($p['MARGIN-RIGHT']) == 'auto') {\n                $bbox_mr = 'auto';\n            }\n            $bbox_ml = $this->blk[1]['margin_left'];\n            if (isset($p['MARGIN-LEFT']) && strtolower($p['MARGIN-LEFT']) == 'auto') {\n                $bbox_ml = 'auto';\n            }\n            $bbox_mt = $this->blk[1]['margin_top'];\n            if (isset($p['MARGIN-TOP']) && strtolower($p['MARGIN-TOP']) == 'auto') {\n                $bbox_mt = 'auto';\n            }\n            $bbox_mb = $this->blk[1]['margin_bottom'];\n            if (isset($p['MARGIN-BOTTOM']) && strtolower($p['MARGIN-BOTTOM']) == 'auto') {\n                $bbox_mb = 'auto';\n            }\n            if (isset($p['LEFT']) && strtolower($p['LEFT']) != 'auto') {\n                $bbox_left = $this->sizeConverter->convert($p['LEFT'], $cont_w, $this->FontSize, false);\n            } else {\n                $bbox_left = 'auto';\n            }\n            if (isset($p['TOP']) && strtolower($p['TOP']) != 'auto') {\n                $bbox_top = $this->sizeConverter->convert($p['TOP'], $cont_h, $this->FontSize, false);\n            } else {\n                $bbox_top = 'auto';\n            }\n            if (isset($p['RIGHT']) && strtolower($p['RIGHT']) != 'auto') {\n                $bbox_right = $this->sizeConverter->convert($p['RIGHT'], $cont_w, $this->FontSize, false);\n            } else {\n                $bbox_right = 'auto';\n            }\n            if (isset($p['BOTTOM']) && strtolower($p['BOTTOM']) != 'auto') {\n                $bbox_bottom = $this->sizeConverter->convert($p['BOTTOM'], $cont_h, $this->FontSize, false);\n            } else {\n                $bbox_bottom = 'auto';\n            }\n            if (isset($p['WIDTH']) && strtolower($p['WIDTH']) != 'auto') {\n                $inner_w = $this->sizeConverter->convert($p['WIDTH'], $cont_w, $this->FontSize, false);\n            } else {\n                $inner_w = 'auto';\n            }\n            if (isset($p['HEIGHT']) && strtolower($p['HEIGHT']) != 'auto') {\n                $inner_h = $this->sizeConverter->convert($p['HEIGHT'], $cont_h, $this->FontSize, false);\n            } else {\n                $inner_h = 'auto';\n            }\n\n            // If bottom or right pos are set and not left / top - save this to adjust rotated block later\n            if ($rotate == 90 || $rotate == -90) { // mPDF 6\n                if ($bbox_left === 'auto' && $bbox_right !== 'auto') {\n                    $rot_rpos = $bbox_right;\n                } else {\n                    $rot_rpos = false;\n                }\n                if ($bbox_top === 'auto' && $bbox_bottom !== 'auto') {\n                    $rot_bpos = $bbox_bottom;\n                } else {\n                    $rot_bpos = false;\n                }\n            }\n\n            // ================================================================\n            if ($checkinnerhtml == '' && $inner_h === 'auto') {\n                $inner_h = 0.0001;\n            }\n            if ($checkinnerhtml == '' && $inner_w === 'auto') {\n                $inner_w = 2 * $this->GetCharWidth('W', false);\n            }\n            // ================================================================\n            // Algorithm from CSS2.1  See http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height\n            // mPD 5.3.14\n            // Special case (not CSS) if all not specified, centre vertically on page\n            $bbox_top_orig = '';\n            if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_mt === 'auto' && $bbox_mb === 'auto') {\n                $bbox_top_orig = $bbox_top;\n                if ($bbox_mt === 'auto') {\n                    $bbox_mt = 0;\n                }\n                if ($bbox_mb === 'auto') {\n                    $bbox_mb = 0;\n                }\n                $bbox_top = $orig_y0 - $bbox_mt - $cont_y;\n                // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'\n            } // mPD 5.3.14\n            elseif ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto') {\n                $bbox_top_orig = $bbox_top = $orig_y0 - $cont_y;\n                if ($bbox_mt === 'auto') {\n                    $bbox_mt = 0;\n                }\n                if ($bbox_mb === 'auto') {\n                    $bbox_mb = 0;\n                }\n                // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'\n            } elseif ($bbox_top !== 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {\n                if ($bbox_mt === 'auto' && $bbox_mb === 'auto') {\n                    $x = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;\n                    $bbox_mt = $bbox_mb = ($x / 2);\n                } elseif ($bbox_mt === 'auto') {\n                    $bbox_mt = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;\n                } elseif ($bbox_mb === 'auto') {\n                    $bbox_mb = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;\n                } else {\n                    $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;\n                }\n            } else {\n                if ($bbox_mt === 'auto') {\n                    $bbox_mt = 0;\n                }\n                if ($bbox_mb === 'auto') {\n                    $bbox_mb = 0;\n                }\n                if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom !== 'auto') {\n                    // solve for $bbox_top when content_h known - $inner_h=='auto' && $bbox_top =='auto'\n                } elseif ($bbox_top === 'auto' && $bbox_bottom === 'auto' && $inner_h !== 'auto') {\n                    $bbox_top = $orig_y0 - $bbox_mt - $cont_y;\n                    $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;\n                } elseif ($inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_top !== 'auto') {\n                    // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'\n                } elseif ($bbox_top === 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {\n                    $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;\n                } elseif ($inner_h === 'auto' && $bbox_top !== 'auto' && $bbox_bottom !== 'auto') {\n                    $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;\n                } elseif ($bbox_bottom === 'auto' && $bbox_top !== 'auto' && $inner_h !== 'auto') {\n                    $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;\n                }\n            }\n\n            // THEN DO SAME FOR WIDTH\n            // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width\n            if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right === 'auto') {\n                if ($bbox_ml === 'auto') {\n                    $bbox_ml = 0;\n                }\n                if ($bbox_mr === 'auto') {\n                    $bbox_mr = 0;\n                }\n                // IF containing element RTL, should set $bbox_right\n                $bbox_left = $orig_x0 - $bbox_ml - $cont_x;\n                // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'\n            } elseif ($bbox_left !== 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {\n                if ($bbox_ml === 'auto' && $bbox_mr === 'auto') {\n                    $x = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;\n                    $bbox_ml = $bbox_mr = ($x / 2);\n                } elseif ($bbox_ml === 'auto') {\n                    $bbox_ml = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;\n                } elseif ($bbox_mr === 'auto') {\n                    $bbox_mr = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;\n                } else {\n                    $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;\n                }\n            } else {\n                if ($bbox_ml === 'auto') {\n                    $bbox_ml = 0;\n                }\n                if ($bbox_mr === 'auto') {\n                    $bbox_mr = 0;\n                }\n                if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right !== 'auto') {\n                    // solve for $bbox_left when content_w known - $inner_w=='auto' && $bbox_left =='auto'\n                } elseif ($bbox_left === 'auto' && $bbox_right === 'auto' && $inner_w !== 'auto') {\n                    // IF containing element RTL, should set $bbox_right\n                    $bbox_left = $orig_x0 - $bbox_ml - $cont_x;\n                    $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;\n                } elseif ($inner_w === 'auto' && $bbox_right === 'auto' && $bbox_left !== 'auto') {\n                    // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'\n                } elseif ($bbox_left === 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {\n                    $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;\n                } elseif ($inner_w === 'auto' && $bbox_left !== 'auto' && $bbox_right !== 'auto') {\n                    $inner_w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;\n                } elseif ($bbox_right === 'auto' && $bbox_left !== 'auto' && $inner_w !== 'auto') {\n                    $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;\n                }\n            }\n\n            // ================================================================\n            // ================================================================\n            /* -- BACKGROUNDS -- */\n            if (isset($pb['BACKGROUND-IMAGE']) && $pb['BACKGROUND-IMAGE']) {\n                $ret = $this->SetBackground($pb, $this->blk[1]['inner_width']);\n                if ($ret) {\n                    $this->blk[1]['background-image'] = $ret;\n                }\n            }\n            /* -- END BACKGROUNDS -- */\n\n            $bbox_top_auto = $bbox_top === 'auto';\n            $bbox_left_auto = $bbox_left === 'auto';\n            $bbox_right_auto = $bbox_right === 'auto';\n            $bbox_bottom_auto = $bbox_bottom === 'auto';\n\n            $bbox_top = is_numeric($bbox_top) ? $bbox_top : 0;\n            $bbox_left = is_numeric($bbox_left) ? $bbox_left : 0;\n            $bbox_right = is_numeric($bbox_right) ? $bbox_right : 0;\n            $bbox_bottom = is_numeric($bbox_bottom) ? $bbox_bottom : 0;\n\n            $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;\n            $h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;\n\n            $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;\n            $w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;\n\n            // Set (temporary) values for x y w h to do first paint, if values are auto\n            if ($inner_h === 'auto' && $bbox_top_auto) {\n                $y = $cont_y + $bbox_mt + $bbox_bt + $bbox_pt;\n                $h = $cont_h - ($bbox_bottom + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);\n            } elseif ($inner_h === 'auto' && $bbox_bottom_auto) {\n                $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;\n                $h = $cont_h - ($bbox_top + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);\n            }\n            if ($inner_w === 'auto' && $bbox_left_auto) {\n                $x = $cont_x + $bbox_ml + $bbox_bl + $bbox_pl;\n                $w = $cont_w - ($bbox_right + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);\n            } elseif ($inner_w === 'auto' && $bbox_right_auto) {\n                $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;\n                $w = $cont_w - ($bbox_left + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);\n            }\n\n            $bbox_y = $cont_y + $bbox_top + $bbox_mt;\n            $bbox_x = $cont_x + $bbox_left + $bbox_ml;\n\n            $saved_block1 = $this->blk[1];\n\n            unset($p);\n            unset($pb);\n\n            // ================================================================\n            if ($inner_w === 'auto') { // do a first write\n                $this->lMargin = $x;\n                $this->rMargin = $this->w - $w - $x;\n\n                // SET POSITION & FONT VALUES\n                $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n                $this->pageoutput[$this->page] = [];\n                $this->x = $x;\n                $this->y = $y;\n                $this->HTMLheaderPageLinks = [];\n                $this->HTMLheaderPageAnnots = [];\n                $this->HTMLheaderPageForms = [];\n                $this->pageBackgrounds = [];\n                $this->maxPosR = 0;\n                $this->maxPosL = $this->w; // For RTL\n                $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);\n                $inner_w = $this->maxPosR - $this->lMargin;\n                if ($bbox_right_auto) {\n                    $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;\n                } elseif ($bbox_left_auto) {\n                    $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;\n                    $bbox_x = $cont_x + $bbox_left + $bbox_ml;\n                    $inner_x = $bbox_x + $bbox_bl + $bbox_pl;\n                    $x = $inner_x;\n                }\n\n                $w = $inner_w;\n                $bbox_y = $cont_y + $bbox_top + $bbox_mt;\n                $bbox_x = $cont_x + $bbox_left + $bbox_ml;\n            }\n\n            if ($inner_h === 'auto') { // do a first write\n\n                $this->lMargin = $x;\n                $this->rMargin = $this->w - $w - $x;\n\n                // SET POSITION & FONT VALUES\n                $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n                $this->pageoutput[$this->page] = [];\n                $this->x = $x;\n                $this->y = $y;\n                $this->HTMLheaderPageLinks = [];\n                $this->HTMLheaderPageAnnots = [];\n                $this->HTMLheaderPageForms = [];\n                $this->pageBackgrounds = [];\n                $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);\n                $inner_h = $this->y - $y;\n\n                if ($overflow != 'hidden' && $overflow != 'visible') { // constrained\n                    if (($this->y + $bbox_pb + $bbox_bb) > ($cont_y + $cont_h)) {\n                        $adj = ($this->y + $bbox_pb + $bbox_bb) - ($cont_y + $cont_h);\n                        $inner_h -= $adj;\n                    }\n                }\n                if ($bbox_bottom_auto && $bbox_top_orig === 'auto') {\n                    $bbox_bottom = $bbox_top = ($cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb) / 2;\n                    if ($overflow != 'hidden' && $overflow != 'visible') { // constrained\n                        if ($bbox_top < 0) {\n                            $bbox_top = 0;\n                            $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;\n                        }\n                    }\n                    $bbox_y = $cont_y + $bbox_top + $bbox_mt;\n                    $inner_y = $bbox_y + $bbox_bt + $bbox_pt;\n                    $y = $inner_y;\n                } elseif ($bbox_bottom_auto) {\n                    $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb;\n                } elseif ($bbox_top_auto) {\n                    $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;\n                    if ($overflow != 'hidden' && $overflow != 'visible') { // constrained\n                        if ($bbox_top < 0) {\n                            $bbox_top = 0;\n                            $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;\n                        }\n                    }\n                    $bbox_y = $cont_y + $bbox_top + $bbox_mt;\n                    $inner_y = $bbox_y + $bbox_bt + $bbox_pt;\n                    $y = $inner_y;\n                }\n                $h = $inner_h;\n                $bbox_y = $cont_y + $bbox_top + $bbox_mt;\n                $bbox_x = $cont_x + $bbox_left + $bbox_ml;\n            }\n\n            $inner_w = $w;\n            $inner_h = $h;\n        }\n\n        $this->lMargin = $x;\n        $this->rMargin = $this->w - $w - $x;\n\n        // SET POSITION & FONT VALUES\n        $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n        $this->pageoutput[$this->page] = [];\n\n        $this->x = $x;\n        $this->y = $y;\n\n        $this->HTMLheaderPageLinks = [];\n        $this->HTMLheaderPageAnnots = [];\n        $this->HTMLheaderPageForms = [];\n\n        $this->pageBackgrounds = [];\n\n        $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);\n\n        $actual_h = $this->y - $y;\n        $use_w = $w;\n        $use_h = $h;\n        $ratio = $actual_h / $use_w;\n\n        if ($overflow != 'hidden' && $overflow != 'visible') {\n            $target = $h / $w;\n            if ($target > 0) {\n                if (($ratio / $target) > 1) {\n                    $nl = ceil($actual_h / $this->lineheight);\n                    $l = $use_w * $nl;\n                    $est_w = sqrt(($l * $this->lineheight) / $target) * 0.8;\n                    $use_w += ($est_w - $use_w) - ($w / 100);\n                }\n                $bpcstart = ($ratio / $target);\n                $bpcctr = 1;\n\n                while (($ratio / $target) > 1) {\n                    // @log 'Auto-sizing fixed-position block $bpcctr++\n\n                    $this->x = $x;\n                    $this->y = $y;\n\n                    if (($ratio / $target) > 1.5 || ($ratio / $target) < 0.6) {\n                        $use_w += ($w / $this->incrementFPR1);\n                    } elseif (($ratio / $target) > 1.2 || ($ratio / $target) < 0.85) {\n                        $use_w += ($w / $this->incrementFPR2);\n                    } elseif (($ratio / $target) > 1.1 || ($ratio / $target) < 0.91) {\n                        $use_w += ($w / $this->incrementFPR3);\n                    } else {\n                        $use_w += ($w / $this->incrementFPR4);\n                    }\n\n                    $use_h = $use_w * $target;\n                    $this->rMargin = $this->w - $use_w - $x;\n                    $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n                    $this->HTMLheaderPageLinks = [];\n                    $this->HTMLheaderPageAnnots = [];\n                    $this->HTMLheaderPageForms = [];\n                    $this->pageBackgrounds = [];\n                    $this->WriteHTML($html, HTMLParserMode::HTML_HEADER_BUFFER);\n                    $actual_h = $this->y - $y;\n                    $ratio = $actual_h / $use_w;\n                }\n            }\n        }\n\n        $shrink_f = $w / $use_w;\n\n        // ================================================================\n\n        $this->pages[$this->page] .= '___BEFORE_BORDERS___';\n        $block_s = $this->PrintPageBackgrounds(); // Save to print later inside clipping path\n        $this->pageBackgrounds = [];\n\n        // ================================================================\n\n        if ($rotate == 90 || $rotate == -90) { // mPDF 6\n            $prerotw = $bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br;\n            $preroth = $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;\n            $rot_start = \" q\\n\";\n            if ($rotate == 90) {\n                if ($rot_rpos !== false) {\n                    $adjw = $prerotw;\n                } // width before rotation\n                else {\n                    $adjw = $preroth;\n                } // height before rotation\n                if ($rot_bpos !== false) {\n                    $adjh = -$prerotw + $preroth;\n                } else {\n                    $adjh = 0;\n                }\n            } else {\n                if ($rot_rpos !== false) {\n                    $adjw = $prerotw - $preroth;\n                } else {\n                    $adjw = 0;\n                }\n                if ($rot_bpos !== false) {\n                    $adjh = $preroth;\n                } // height before rotation\n                else {\n                    $adjh = $prerotw;\n                } // width before rotation\n            }\n            $rot_start .= $this->transformTranslate($adjw, $adjh, true) . \"\\n\";\n            $rot_start .= $this->transformRotate($rotate, $bbox_x, $bbox_y, true) . \"\\n\";\n            $rot_end = \" Q\\n\";\n        } elseif ($rotate == 180) { // mPDF 6\n            $rot_start = \" q\\n\";\n            $rot_start .= $this->transformTranslate($bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br, $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb, true) . \"\\n\";\n            $rot_start .= $this->transformRotate(180, $bbox_x, $bbox_y, true) . \"\\n\";\n            $rot_end = \" Q\\n\";\n        } else {\n            $rot_start = '';\n            $rot_end = '';\n        }\n\n        // ================================================================\n        if (!empty($bounding)) {\n            // WHEN HEIGHT // BOTTOM EDGE IS KNOWN and $this->y is set to the bottom\n            // Re-instate saved $this->blk[1]\n            $this->blk[1] = $saved_block1;\n\n            // These are only needed when painting border/background\n            $this->blk[1]['width'] = $bbox_w = $cont_w - $bbox_left - $bbox_ml - $bbox_mr - $bbox_right;\n            $this->blk[1]['x0'] = $bbox_x;\n            $this->blk[1]['y0'] = $bbox_y;\n            $this->blk[1]['startpage'] = $this->page;\n            $this->blk[1]['y1'] = $bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;\n            $this->writer->write($rot_start);\n            $this->PaintDivBB('', 0, 1); // Prints borders and sets backgrounds in $this->pageBackgrounds\n            $this->writer->write($rot_end);\n        }\n\n        $s = $this->PrintPageBackgrounds();\n        $s = $rot_start . $s . $rot_end;\n        $this->pages[$this->page] = preg_replace('/___BEFORE_BORDERS___/', \"\\n\" . $s . \"\\n\", $this->pages[$this->page]);\n        $this->pageBackgrounds = [];\n\n        $this->writer->write($rot_start);\n\n        // Clipping Output\n        if ($overflow == 'hidden') {\n            // Bounding rectangle to clip\n            $clip_y1 = $this->y;\n            if (!empty($bounding) && ($this->y + $bbox_pb + $bbox_bb) > ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb )) {\n                $clip_y1 = ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb ) - ($bbox_pb + $bbox_bb);\n            }\n            // $op = 'W* n';\t// Clipping\n            $op = 'W n'; // Clipping alternative mode\n            $this->writer->write(\"q\");\n            $ch = $clip_y1 - $y;\n            $this->writer->write(sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$ch * Mpdf::SCALE, $op));\n            if (!empty($block_s)) {\n                $tmp = \"q\\n\" . sprintf('%.3F %.3F %.3F %.3F re %s', $x * Mpdf::SCALE, ($this->h - $y) * Mpdf::SCALE, $w * Mpdf::SCALE, -$ch * Mpdf::SCALE, $op);\n                $tmp .= \"\\n\" . $block_s . \"\\nQ\";\n                $block_s = $tmp;\n            }\n        }\n\n\n        if (!empty($block_s)) {\n            if ($shrink_f != 1) { // i.e. autofit has resized the box\n                $tmp = \"q\\n\" . $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y, true);\n                $tmp .= \"\\n\" . $block_s . \"\\nQ\";\n                $block_s = $tmp;\n            }\n            $this->writer->write($block_s);\n        }\n\n\n\n        if ($shrink_f != 1) { // i.e. autofit has resized the box\n            $this->StartTransform();\n            $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y);\n        }\n\n        $this->writer->write($this->headerbuffer);\n\n        if ($shrink_f != 1) { // i.e. autofit has resized the box\n            $this->StopTransform();\n        }\n\n        if ($overflow == 'hidden') {\n            // End clipping\n            $this->writer->write(\"Q\");\n        }\n\n        $this->writer->write($rot_end);\n\n\n        // Page Links\n        foreach ($this->HTMLheaderPageLinks as $lk) {\n            if ($rotate) {\n                $tmp = $lk[2]; // Switch h - w\n                $lk[2] = $lk[3];\n                $lk[3] = $tmp;\n\n                $lx1 = (($lk[0] / Mpdf::SCALE));\n                $ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)));\n                if ($rotate == 90) {\n                    $adjx = -($lx1 - $bbox_x) + ($preroth - ($ly1 - $bbox_y));\n                    $adjy = -($ly1 - $bbox_y) + ($lx1 - $bbox_x);\n                    $lk[2] = -$lk[2];\n                } elseif ($rotate == -90) {\n                    $adjx = -($lx1 - $bbox_x) + ($ly1 - $bbox_y);\n                    $adjy = -($ly1 - $bbox_y) - ($lx1 - $bbox_x) + $prerotw;\n                    $lk[3] = -$lk[3];\n                }\n                if ($rot_rpos !== false) {\n                    $adjx += $prerotw - $preroth;\n                }\n                if ($rot_bpos !== false) {\n                    $adjy += $preroth - $prerotw;\n                }\n                $lx1 += $adjx;\n                $ly1 += $adjy;\n\n                $lk[0] = $lx1 * Mpdf::SCALE;\n                $lk[1] = ($this->h - $ly1) * Mpdf::SCALE;\n            }\n            if ($shrink_f != 1) {  // i.e. autofit has resized the box\n                $lx1 = (($lk[0] / Mpdf::SCALE) - $x);\n                $lx2 = $x + ($lx1 * $shrink_f);\n                $lk[0] = $lx2 * Mpdf::SCALE;\n                $ly1 = (($this->h - ($lk[1] / Mpdf::SCALE)) - $y);\n                $ly2 = $y + ($ly1 * $shrink_f);\n                $lk[1] = ($this->h - $ly2) * Mpdf::SCALE;\n                $lk[2] *= $shrink_f; // width\n                $lk[3] *= $shrink_f; // height\n            }\n            $this->PageLinks[$this->page][] = $lk;\n        }\n\n        foreach ($this->HTMLheaderPageForms as $n => $f) {\n            if ($shrink_f != 1) {  // i.e. autofit has resized the box\n                $f['x'] = $x + (($f['x'] - $x) * $shrink_f);\n                $f['y'] = $y + (($f['y'] - $y) * $shrink_f);\n                $f['w'] *= $shrink_f;\n                $f['h'] *= $shrink_f;\n                $f['style']['fontsize'] *= $shrink_f;\n            }\n            $this->form->forms[$f['n']] = $f;\n        }\n        // Page Annotations\n        foreach ($this->HTMLheaderPageAnnots as $lk) {\n            if ($rotate) {\n                if ($rotate == 90) {\n                    $adjx = -($lk['x'] - $bbox_x) + ($preroth - ($lk['y'] - $bbox_y));\n                    $adjy = -($lk['y'] - $bbox_y) + ($lk['x'] - $bbox_x);\n                } elseif ($rotate == -90) {\n                    $adjx = -($lk['x'] - $bbox_x) + ($lk['y'] - $bbox_y);\n                    $adjy = -($lk['y'] - $bbox_y) - ($lk['x'] - $bbox_x) + $prerotw;\n                }\n                if ($rot_rpos !== false) {\n                    $adjx += $prerotw - $preroth;\n                }\n                if ($rot_bpos !== false) {\n                    $adjy += $preroth - $prerotw;\n                }\n                $lk['x'] += $adjx;\n                $lk['y'] += $adjy;\n            }\n            if ($shrink_f != 1) {  // i.e. autofit has resized the box\n                $lk['x'] = $x + (($lk['x'] - $x) * $shrink_f);\n                $lk['y'] = $y + (($lk['y'] - $y) * $shrink_f);\n            }\n            $this->PageAnnots[$this->page][] = $lk;\n        }\n\n        // Restore\n        $this->headerbuffer = '';\n        $this->HTMLheaderPageLinks = [];\n        $this->HTMLheaderPageAnnots = [];\n        $this->HTMLheaderPageForms = [];\n        $this->pageBackgrounds = $save_bgs;\n        $this->writingHTMLheader = false;\n\n        $this->writingHTMLfooter = false;\n        $this->fullImageHeight = false;\n        $this->ResetMargins();\n        $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n        $this->SetXY($save_x, $save_y);\n        $this->title2annots = $save_annots; // *ANNOTATIONS*\n        $this->InFooter = false; // turns back on autopagebreaks\n        $this->pageoutput[$this->page] = [];\n        $this->pageoutput[$this->page]['Font'] = '';\n        /* -- COLUMNS -- */\n        if ($save_cols) {\n            $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);\n        }\n        /* -- END COLUMNS -- */\n    }\n\n    /* -- END CSS-POSITION -- */\n\n    function initialiseBlock(&$blk)\n    {\n        $blk['margin_top'] = 0;\n        $blk['margin_left'] = 0;\n        $blk['margin_bottom'] = 0;\n        $blk['margin_right'] = 0;\n        $blk['padding_top'] = 0;\n        $blk['padding_left'] = 0;\n        $blk['padding_bottom'] = 0;\n        $blk['padding_right'] = 0;\n        $blk['border_top']['w'] = 0;\n        $blk['border_left']['w'] = 0;\n        $blk['border_bottom']['w'] = 0;\n        $blk['border_right']['w'] = 0;\n        $blk['direction'] = 'ltr';\n        $blk['hide'] = false;\n        $blk['outer_left_margin'] = 0;\n        $blk['outer_right_margin'] = 0;\n        $blk['cascadeCSS'] = [];\n        $blk['block-align'] = false;\n        $blk['bgcolor'] = false;\n        $blk['page_break_after_avoid'] = false;\n        $blk['keep_block_together'] = false;\n        $blk['float'] = false;\n        $blk['line_height'] = '';\n        $blk['margin_collapse'] = false;\n    }\n\n    function border_details($bd)\n    {\n        $prop = preg_split('/\\s+/', trim($bd));\n\n        if (isset($this->blk[$this->blklvl]['inner_width'])) {\n            $refw = $this->blk[$this->blklvl]['inner_width'];\n        } elseif (isset($this->blk[$this->blklvl - 1]['inner_width'])) {\n            $refw = $this->blk[$this->blklvl - 1]['inner_width'];\n        } else {\n            $refw = $this->w;\n        }\n        if (count($prop) == 1) {\n            $bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);\n            if ($bsize > 0) {\n                return ['s' => 1, 'w' => $bsize, 'c' => $this->colorConverter->convert(0, $this->PDFAXwarnings), 'style' => 'solid'];\n            } else {\n                return ['w' => 0, 's' => 0];\n            }\n        } elseif (count($prop) == 2) {\n            // 1px solid\n            if (in_array($prop[1], $this->borderstyles) || $prop[1] == 'none' || $prop[1] == 'hidden') {\n                $prop[2] = '';\n            } // solid #000000\n            elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {\n                $prop[0] = '';\n                $prop[1] = $prop[0];\n                $prop[2] = $prop[1];\n            } // 1px #000000\n            else {\n                $prop[1] = '';\n                $prop[2] = $prop[1];\n            }\n        } elseif (count($prop) == 3) {\n            // Change #000000 1px solid to 1px solid #000000 (proper)\n            if (substr($prop[0], 0, 1) == '#') {\n                $tmp = $prop[0];\n                $prop[0] = $prop[1];\n                $prop[1] = $prop[2];\n                $prop[2] = $tmp;\n            } // Change solid #000000 1px to 1px solid #000000 (proper)\n            elseif (substr($prop[0], 1, 1) == '#') {\n                $tmp = $prop[1];\n                $prop[0] = $prop[2];\n                $prop[1] = $prop[0];\n                $prop[2] = $tmp;\n            } // Change solid 1px #000000 to 1px solid #000000 (proper)\n            elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {\n                $tmp = $prop[0];\n                $prop[0] = $prop[1];\n                $prop[1] = $tmp;\n            }\n        } else {\n            return ['w' => 0, 's' => 0];\n        }\n        // Size\n        $bsize = $this->sizeConverter->convert($prop[0], $refw, $this->FontSize, false);\n        // color\n        $coul = $this->colorConverter->convert($prop[2], $this->PDFAXwarnings); // returns array\n        // Style\n        $prop[1] = strtolower($prop[1]);\n        if (in_array($prop[1], $this->borderstyles) && $bsize > 0) {\n            $on = 1;\n        } elseif ($prop[1] == 'hidden') {\n            $on = 1;\n            $bsize = 0;\n            $coul = '';\n        } elseif ($prop[1] == 'none') {\n            $on = 0;\n            $bsize = 0;\n            $coul = '';\n        } else {\n            $on = 0;\n            $bsize = 0;\n            $coul = '';\n            $prop[1] = '';\n        }\n        return ['s' => $on, 'w' => $bsize, 'c' => $coul, 'style' => $prop[1], 'dom' => 0];\n    }\n\n    /* -- END HTML-CSS -- */\n\n\n    /* -- BORDER-RADIUS -- */\n\n    function _borderPadding($a, $b, &$px, &$py)\n    {\n        // $px and py are padding long axis (x) and short axis (y)\n        $added = 0; // extra padding\n\n        $x = $a - $px;\n        $y = $b - $py;\n        // Check if Falls within ellipse of border radius\n        if (( (($x + $added) * ($x + $added)) / ($a * $a) + (($y + $added) * ($y + $added)) / ($b * $b) ) <= 1) {\n            return false;\n        }\n\n        $t = atan2($y, $x);\n\n        $newx = $b / sqrt((($b * $b) / ($a * $a)) + ( tan($t) * tan($t) ));\n        $newy = $a / sqrt((($a * $a) / ($b * $b)) + ( (1 / tan($t)) * (1 / tan($t)) ));\n        $px = max($px, $a - $newx + $added);\n        $py = max($py, $b - $newy + $added);\n    }\n\n    /* -- END BORDER-RADIUS -- */\n    /* -- HTML-CSS -- */\n    /* -- CSS-PAGE -- */\n\n    function SetPagedMediaCSS($name, $first, $oddEven)\n    {\n        if ($oddEven == 'E') {\n            if ($this->directionality == 'rtl') {\n                $side = 'R';\n            } else {\n                $side = 'L';\n            }\n        } else {\n            if ($this->directionality == 'rtl') {\n                $side = 'L';\n            } else {\n                $side = 'R';\n            }\n        }\n        $name = strtoupper($name);\n        $p = [];\n        $p['SIZE'] = 'AUTO';\n\n        // Uses mPDF original margins as default\n        $p['MARGIN-RIGHT'] = strval($this->orig_rMargin) . 'mm';\n        $p['MARGIN-LEFT'] = strval($this->orig_lMargin) . 'mm';\n        $p['MARGIN-TOP'] = strval($this->orig_tMargin) . 'mm';\n        $p['MARGIN-BOTTOM'] = strval($this->orig_bMargin) . 'mm';\n        $p['MARGIN-HEADER'] = strval($this->orig_hMargin) . 'mm';\n        $p['MARGIN-FOOTER'] = strval($this->orig_fMargin) . 'mm';\n\n        // Basic page + selector\n        if (isset($this->cssManager->CSS['@PAGE'])) {\n            $zp = $this->cssManager->CSS['@PAGE'];\n        } else {\n            $zp = [];\n        }\n        if (is_array($zp) && !empty($zp)) {\n            $p = array_merge($p, $zp);\n        }\n\n        if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {\n            $p['HEADER'] = $p['EVEN-HEADER-NAME'];\n            unset($p['EVEN-HEADER-NAME']);\n        }\n        if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {\n            $p['HEADER'] = $p['ODD-HEADER-NAME'];\n            unset($p['ODD-HEADER-NAME']);\n        }\n        if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {\n            $p['FOOTER'] = $p['EVEN-FOOTER-NAME'];\n            unset($p['EVEN-FOOTER-NAME']);\n        }\n        if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {\n            $p['FOOTER'] = $p['ODD-FOOTER-NAME'];\n            unset($p['ODD-FOOTER-NAME']);\n        }\n\n        // If right/Odd page\n        if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT']) && $side == 'R') {\n            $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>RIGHT'];\n        } else {\n            $zp = [];\n        }\n        if (isset($zp['SIZE'])) {\n            unset($zp['SIZE']);\n        }\n        if (isset($zp['SHEET-SIZE'])) {\n            unset($zp['SHEET-SIZE']);\n        }\n        // Disallow margin-left or -right on :LEFT or :RIGHT\n        if (isset($zp['MARGIN-LEFT'])) {\n            unset($zp['MARGIN-LEFT']);\n        }\n        if (isset($zp['MARGIN-RIGHT'])) {\n            unset($zp['MARGIN-RIGHT']);\n        }\n        if (is_array($zp) && !empty($zp)) {\n            $p = array_merge($p, $zp);\n        }\n\n        // If left/Even page\n        if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT']) && $side == 'L') {\n            $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>LEFT'];\n        } else {\n            $zp = [];\n        }\n        if (isset($zp['SIZE'])) {\n            unset($zp['SIZE']);\n        }\n        if (isset($zp['SHEET-SIZE'])) {\n            unset($zp['SHEET-SIZE']);\n        }\n        // Disallow margin-left or -right on :LEFT or :RIGHT\n        if (isset($zp['MARGIN-LEFT'])) {\n            unset($zp['MARGIN-LEFT']);\n        }\n        if (isset($zp['MARGIN-RIGHT'])) {\n            unset($zp['MARGIN-RIGHT']);\n        }\n        if (is_array($zp) && !empty($zp)) {\n            $p = array_merge($p, $zp);\n        }\n\n        // If first page\n        if (isset($this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST']) && $first) {\n            $zp = $this->cssManager->CSS['@PAGE>>PSEUDO>>FIRST'];\n        } else {\n            $zp = [];\n        }\n        if (isset($zp['SIZE'])) {\n            unset($zp['SIZE']);\n        }\n        if (isset($zp['SHEET-SIZE'])) {\n            unset($zp['SHEET-SIZE']);\n        }\n        // Disallow margin-left or -right on :FIRST\t// mPDF 5.7.3\n        if (isset($zp['MARGIN-LEFT'])) {\n            unset($zp['MARGIN-LEFT']);\n        }\n        if (isset($zp['MARGIN-RIGHT'])) {\n            unset($zp['MARGIN-RIGHT']);\n        }\n        if (is_array($zp) && !empty($zp)) {\n            $p = array_merge($p, $zp);\n        }\n\n        // If named page\n        if ($name) {\n            if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name])) {\n                $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name];\n            } else {\n                $zp = [];\n            }\n            if (is_array($zp) && !empty($zp)) {\n                $p = array_merge($p, $zp);\n            }\n\n            if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {\n                $p['HEADER'] = $p['EVEN-HEADER-NAME'];\n                unset($p['EVEN-HEADER-NAME']);\n            }\n            if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {\n                $p['HEADER'] = $p['ODD-HEADER-NAME'];\n                unset($p['ODD-HEADER-NAME']);\n            }\n            if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {\n                $p['FOOTER'] = $p['EVEN-FOOTER-NAME'];\n                unset($p['EVEN-FOOTER-NAME']);\n            }\n            if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {\n                $p['FOOTER'] = $p['ODD-FOOTER-NAME'];\n                unset($p['ODD-FOOTER-NAME']);\n            }\n\n            // If named right/Odd page\n            if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT']) && $side == 'R') {\n                $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT'];\n            } else {\n                $zp = [];\n            }\n            if (isset($zp['SIZE'])) {\n                unset($zp['SIZE']);\n            }\n            if (isset($zp['SHEET-SIZE'])) {\n                unset($zp['SHEET-SIZE']);\n            }\n            // Disallow margin-left or -right on :LEFT or :RIGHT\n            if (isset($zp['MARGIN-LEFT'])) {\n                unset($zp['MARGIN-LEFT']);\n            }\n            if (isset($zp['MARGIN-RIGHT'])) {\n                unset($zp['MARGIN-RIGHT']);\n            }\n            if (is_array($zp) && !empty($zp)) {\n                $p = array_merge($p, $zp);\n            }\n\n            // If named left/Even page\n            if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT']) && $side == 'L') {\n                $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT'];\n            } else {\n                $zp = [];\n            }\n            if (isset($zp['SIZE'])) {\n                unset($zp['SIZE']);\n            }\n            if (isset($zp['SHEET-SIZE'])) {\n                unset($zp['SHEET-SIZE']);\n            }\n            // Disallow margin-left or -right on :LEFT or :RIGHT\n            if (isset($zp['MARGIN-LEFT'])) {\n                unset($zp['MARGIN-LEFT']);\n            }\n            if (isset($zp['MARGIN-RIGHT'])) {\n                unset($zp['MARGIN-RIGHT']);\n            }\n            if (is_array($zp) && !empty($zp)) {\n                $p = array_merge($p, $zp);\n            }\n\n            // If named first page\n            if (isset($this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST']) && $first) {\n                $zp = $this->cssManager->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST'];\n            } else {\n                $zp = [];\n            }\n            if (isset($zp['SIZE'])) {\n                unset($zp['SIZE']);\n            }\n            if (isset($zp['SHEET-SIZE'])) {\n                unset($zp['SHEET-SIZE']);\n            }\n            // Disallow margin-left or -right on :FIRST\t// mPDF 5.7.3\n            if (isset($zp['MARGIN-LEFT'])) {\n                unset($zp['MARGIN-LEFT']);\n            }\n            if (isset($zp['MARGIN-RIGHT'])) {\n                unset($zp['MARGIN-RIGHT']);\n            }\n            if (is_array($zp) && !empty($zp)) {\n                $p = array_merge($p, $zp);\n            }\n        }\n\n        $orientation = $mgl = $mgr = $mgt = $mgb = $mgh = $mgf = '';\n        $header = $footer = '';\n        $resetpagenum = $pagenumstyle = $suppress = '';\n        $marks = '';\n        $bg = [];\n\n        $newformat = '';\n\n\n        if (isset($p['SHEET-SIZE']) && is_array($p['SHEET-SIZE'])) {\n            $newformat = $p['SHEET-SIZE'];\n            if ($newformat[0] > $newformat[1]) { // landscape\n                $newformat = array_reverse($newformat);\n                $p['ORIENTATION'] = 'L';\n            } else {\n                $p['ORIENTATION'] = 'P';\n            }\n            $this->_setPageSize($newformat, $p['ORIENTATION']);\n        }\n\n        if (isset($p['SIZE']) && is_array($p['SIZE']) && !$newformat) {\n            if ($p['SIZE']['W'] > $p['SIZE']['H']) {\n                $p['ORIENTATION'] = 'L';\n            } else {\n                $p['ORIENTATION'] = 'P';\n            }\n        }\n        if (is_array($p['SIZE'])) {\n            if ($p['SIZE']['W'] > $this->fw) {\n                $p['SIZE']['W'] = $this->fw;\n            } // mPD 4.2 use fw not fPt\n            if ($p['SIZE']['H'] > $this->fh) {\n                $p['SIZE']['H'] = $this->fh;\n            }\n            if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {\n                $outer_width_LR = ($this->fw - $p['SIZE']['W']) / 2;\n                $outer_width_TB = ($this->fh - $p['SIZE']['H']) / 2;\n            } else {\n                $outer_width_LR = ($this->fh - $p['SIZE']['W']) / 2;\n                $outer_width_TB = ($this->fw - $p['SIZE']['H']) / 2;\n            }\n            $pgw = $p['SIZE']['W'];\n            $pgh = $p['SIZE']['H'];\n        } else { // AUTO LANDSCAPE PORTRAIT\n            $outer_width_LR = 0;\n            $outer_width_TB = 0;\n            if (!$newformat) {\n                if (strtoupper($p['SIZE']) == 'AUTO') {\n                    $p['ORIENTATION'] = $this->DefOrientation;\n                } elseif (strtoupper($p['SIZE']) == 'LANDSCAPE') {\n                    $p['ORIENTATION'] = 'L';\n                } else {\n                    $p['ORIENTATION'] = 'P';\n                }\n            }\n            if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {\n                $pgw = $this->fw;\n                $pgh = $this->fh;\n            } else {\n                $pgw = $this->fh;\n                $pgh = $this->fw;\n            }\n        }\n\n        if (isset($p['HEADER']) && $p['HEADER']) {\n            $header = $p['HEADER'];\n        }\n        if (isset($p['FOOTER']) && $p['FOOTER']) {\n            $footer = $p['FOOTER'];\n        }\n        if (isset($p['RESETPAGENUM']) && $p['RESETPAGENUM']) {\n            $resetpagenum = $p['RESETPAGENUM'];\n        }\n        if (isset($p['PAGENUMSTYLE']) && $p['PAGENUMSTYLE']) {\n            $pagenumstyle = $p['PAGENUMSTYLE'];\n        }\n        if (isset($p['SUPPRESS']) && $p['SUPPRESS']) {\n            $suppress = $p['SUPPRESS'];\n        }\n\n        if (isset($p['MARKS'])) {\n            if (preg_match('/cross/i', $p['MARKS']) && preg_match('/crop/i', $p['MARKS'])) {\n                $marks = 'CROPCROSS';\n            } elseif (strtoupper($p['MARKS']) == 'CROP') {\n                $marks = 'CROP';\n            } elseif (strtoupper($p['MARKS']) == 'CROSS') {\n                $marks = 'CROSS';\n            }\n        }\n\n        if (isset($p['BACKGROUND-COLOR']) && $p['BACKGROUND-COLOR']) {\n            $bg['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];\n        }\n        /* -- BACKGROUNDS -- */\n        if (isset($p['BACKGROUND-GRADIENT']) && $p['BACKGROUND-GRADIENT']) {\n            $bg['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];\n        }\n        if (isset($p['BACKGROUND-IMAGE']) && $p['BACKGROUND-IMAGE']) {\n            $bg['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];\n        }\n        if (isset($p['BACKGROUND-REPEAT']) && $p['BACKGROUND-REPEAT']) {\n            $bg['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];\n        }\n        if (isset($p['BACKGROUND-POSITION']) && $p['BACKGROUND-POSITION']) {\n            $bg['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];\n        }\n        if (isset($p['BACKGROUND-IMAGE-RESIZE']) && $p['BACKGROUND-IMAGE-RESIZE']) {\n            $bg['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];\n        }\n        if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {\n            $bg['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];\n        }\n        /* -- END BACKGROUNDS -- */\n\n        if (isset($p['MARGIN-LEFT'])) {\n            $mgl = $this->sizeConverter->convert($p['MARGIN-LEFT'], $pgw) + $outer_width_LR;\n        }\n        if (isset($p['MARGIN-RIGHT'])) {\n            $mgr = $this->sizeConverter->convert($p['MARGIN-RIGHT'], $pgw) + $outer_width_LR;\n        }\n        if (isset($p['MARGIN-BOTTOM'])) {\n            $mgb = $this->sizeConverter->convert($p['MARGIN-BOTTOM'], $pgh) + $outer_width_TB;\n        }\n        if (isset($p['MARGIN-TOP'])) {\n            $mgt = $this->sizeConverter->convert($p['MARGIN-TOP'], $pgh) + $outer_width_TB;\n        }\n        if (isset($p['MARGIN-HEADER'])) {\n            $mgh = $this->sizeConverter->convert($p['MARGIN-HEADER'], $pgh) + $outer_width_TB;\n        }\n        if (isset($p['MARGIN-FOOTER'])) {\n            $mgf = $this->sizeConverter->convert($p['MARGIN-FOOTER'], $pgh) + $outer_width_TB;\n        }\n\n        if (isset($p['ORIENTATION']) && $p['ORIENTATION']) {\n            $orientation = $p['ORIENTATION'];\n        }\n        $this->page_box['outer_width_LR'] = $outer_width_LR; // Used in MARKS:crop etc.\n        $this->page_box['outer_width_TB'] = $outer_width_TB;\n\n        return [$orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $header, $footer, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat];\n    }\n\n    /* -- END CSS-PAGE -- */\n\n\n\n    /* -- CSS-FLOAT -- */\n\n    // Added mPDF 3.0 Float DIV - CLEAR\n    function ClearFloats($clear, $blklvl = 0)\n    {\n        [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width] = $this->GetFloatDivInfo($blklvl, true);\n        $end = $currpos = ($this->page * 1000 + $this->y);\n        if ($clear == 'BOTH' && ($l_exists || $r_exists)) {\n            $this->pageoutput[$this->page] = [];\n            $end = max($l_max, $r_max, $currpos);\n        } elseif ($clear == 'RIGHT' && $r_exists) {\n            $this->pageoutput[$this->page] = [];\n            $end = max($r_max, $currpos);\n        } elseif ($clear == 'LEFT' && $l_exists) {\n            $this->pageoutput[$this->page] = [];\n            $end = max($l_max, $currpos);\n        } else {\n            return;\n        }\n        $old_page = $this->page;\n        $new_page = intval($end / 1000);\n        if ($old_page != $new_page) {\n            $s = $this->PrintPageBackgrounds();\n            // Writes after the marker so not overwritten later by page background etc.\n            $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\\\1' . \"\\n\" . $s . \"\\n\", $this->pages[$this->page]);\n            $this->pageBackgrounds = [];\n            $this->page = $new_page;\n        }\n        $this->ResetMargins();\n        $this->pageoutput[$this->page] = [];\n        $this->y = (($end * 1000) % 1000000) / 1000; // mod changes operands to integers before processing\n    }\n\n    // Added mPDF 3.0 Float DIV\n    function GetFloatDivInfo($blklvl = 0, $clear = false)\n    {\n        // If blklvl specified, only returns floats at that level - for ClearFloats\n        $l_exists = false;\n        $r_exists = false;\n        $l_max = 0;\n        $r_max = 0;\n        $l_width = 0;\n        $r_width = 0;\n        if (count($this->floatDivs)) {\n            $currpos = ($this->page * 1000 + $this->y);\n            foreach ($this->floatDivs as $f) {\n                if (($clear && $f['blockContext'] == $this->blk[$blklvl]['blockContext']) || (!$clear && $currpos >= $f['startpos'] && $currpos < ($f['endpos'] - 0.001) && $f['blklvl'] > $blklvl && $f['blockContext'] == $this->blk[$blklvl]['blockContext'])) {\n                    if ($f['side'] == 'L') {\n                        $l_exists = true;\n                        $l_max = max($l_max, $f['endpos']);\n                        $l_width = max($l_width, $f['w']);\n                    }\n                    if ($f['side'] == 'R') {\n                        $r_exists = true;\n                        $r_max = max($r_max, $f['endpos']);\n                        $r_width = max($r_width, $f['w']);\n                    }\n                }\n            }\n        }\n        return [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width];\n    }\n\n    /* -- END CSS-FLOAT -- */\n\n    // LIST MARKERS\t// mPDF 6  Lists\n    function _setListMarker($listitemtype, $listitemimage, $listitemposition)\n    {\n        // if position:inside (and NOT table) - output now as a textbuffer; (so if next is block, will move to new line)\n        // elseif position:outside (and NOT table) - output in front of first textbuffer output by setting listitem (cf. _saveTextBuffer)\n        $e = '';\n        $this->listitem = '';\n        $spacer = ' ';\n        // IMAGE\n        if ($listitemimage && $listitemimage != 'none') {\n            $listitemimage = trim(preg_replace('/url\\([\"\\']*(.*?)[\"\\']*\\)/', '\\\\1', $listitemimage));\n\n            // ? Restrict maximum height/width of list marker??\n            $maxWidth = 100;\n            $maxHeight = 100;\n\n            $objattr = [];\n            $objattr['margin_top'] = 0;\n            $objattr['margin_bottom'] = 0;\n            $objattr['margin_left'] = 0;\n            $objattr['margin_right'] = 0;\n            $objattr['padding_top'] = 0;\n            $objattr['padding_bottom'] = 0;\n            $objattr['padding_left'] = 0;\n            $objattr['padding_right'] = 0;\n            $objattr['width'] = 0;\n            $objattr['height'] = 0;\n            $objattr['border_top']['w'] = 0;\n            $objattr['border_bottom']['w'] = 0;\n            $objattr['border_left']['w'] = 0;\n            $objattr['border_right']['w'] = 0;\n            $objattr['visibility'] = 'visible';\n            $srcpath = $listitemimage;\n            $orig_srcpath = $listitemimage;\n\n            $objattr['vertical-align'] = 'BS'; // vertical alignment of marker (baseline)\n            $w = 0;\n            $h = 0;\n\n            // Image file\n            $info = $this->imageProcessor->getImage($srcpath, true, true, $orig_srcpath);\n            if (!$info) {\n                return;\n            }\n\n            if ($info['w'] == 0 && $info['h'] == 0) {\n                $info['h'] = $this->sizeConverter->convert('1em', $this->blk[$this->blklvl]['inner_width'], $this->FontSize, false);\n            }\n\n            $objattr['file'] = $srcpath;\n\n            // Default width and height calculation if needed\n            if ($w == 0 and $h == 0) {\n                /* -- IMAGES-WMF -- */\n                if ($info['type'] == 'wmf') {\n                    // WMF units are twips (1/20pt)\n                    // divide by 20 to get points\n                    // divide by k to get user units\n                    $w = abs($info['w']) / (20 * Mpdf::SCALE);\n                    $h = abs($info['h']) / (20 * Mpdf::SCALE);\n                } else { \t\t\t\t/* -- END IMAGES-WMF -- */\n                    if ($info['type'] == 'svg') {\n                        // SVG units are pixels\n                        $w = abs($info['w']) / Mpdf::SCALE;\n                        $h = abs($info['h']) / Mpdf::SCALE;\n                    } else {\n                        // Put image at default image dpi\n                        $w = ($info['w'] / Mpdf::SCALE) * (72 / $this->img_dpi);\n                        $h = ($info['h'] / Mpdf::SCALE) * (72 / $this->img_dpi);\n                    }\n                }\n            }\n            // IF WIDTH OR HEIGHT SPECIFIED\n            if ($w == 0) {\n                $w = abs($h * $info['w'] / $info['h']);\n            }\n            if ($h == 0) {\n                $h = abs($w * $info['h'] / $info['w']);\n            }\n\n            if ($w > $maxWidth) {\n                $w = $maxWidth;\n                $h = abs($w * $info['h'] / $info['w']);\n            }\n\n            if ($h > $maxHeight) {\n                $h = $maxHeight;\n                $w = abs($h * $info['w'] / $info['h']);\n            }\n\n            $objattr['type'] = 'image';\n            $objattr['itype'] = $info['type'];\n\n            $objattr['orig_h'] = $info['h'];\n            $objattr['orig_w'] = $info['w'];\n\n            /* -- IMAGES-WMF -- */\n            if ($info['type'] == 'wmf') {\n                $objattr['wmf_x'] = $info['x'];\n                $objattr['wmf_y'] = $info['y'];\n            } else { \t\t\t/* -- END IMAGES-WMF -- */\n                if ($info['type'] == 'svg') {\n                    $objattr['wmf_x'] = $info['x'];\n                    $objattr['wmf_y'] = $info['y'];\n                }\n            }\n\n            $objattr['height'] = $h;\n            $objattr['width'] = $w;\n            $objattr['image_height'] = $h;\n            $objattr['image_width'] = $w;\n\n            $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');\n            $objattr['listmarker'] = true;\n\n            $objattr['listmarkerposition'] = $listitemposition;\n\n            $e = \"\\xbb\\xa4\\xactype=image,objattr=\" . serialize($objattr) . \"\\xbb\\xa4\\xac\";\n            $this->_saveTextBuffer($e);\n\n            if ($listitemposition == 'inside') {\n                $e = $spacer;\n                $this->_saveTextBuffer($e);\n            }\n        } elseif ($listitemtype == 'disc' || $listitemtype == 'circle' || $listitemtype == 'square') { // SYMBOL (needs new font)\n            $objattr = [];\n            $objattr['type'] = 'listmarker';\n            $objattr['listmarkerposition'] = $listitemposition;\n            $objattr['width'] = 0;\n            $size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);\n            $objattr['size'] = $size;\n            $objattr['offset'] = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);\n\n            if ($listitemposition == 'inside') {\n                $objattr['width'] = $size + $objattr['offset'];\n            }\n\n            $objattr['height'] = $this->FontSize;\n            $objattr['vertical-align'] = 'T';\n            $objattr['text'] = '';\n            $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');\n            $objattr['bullet'] = $listitemtype;\n            $objattr['colorarray'] = $this->colorarray;\n            $objattr['fontfamily'] = $this->FontFamily;\n            $objattr['fontsize'] = $this->FontSize;\n            $objattr['fontsizept'] = $this->FontSizePt;\n            $objattr['fontstyle'] = $this->FontStyle;\n\n            $e = \"\\xbb\\xa4\\xactype=listmarker,objattr=\" . serialize($objattr) . \"\\xbb\\xa4\\xac\";\n            $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array\n\n        } elseif (preg_match('/U\\+([a-fA-F0-9]+)/i', $listitemtype, $m)) { // SYMBOL 2 (needs new font)\n\n            if ($this->_charDefined($this->CurrentFont['cw'], hexdec($m[1]))) {\n                $list_item_marker = UtfString::codeHex2utf($m[1]);\n            } else {\n                $list_item_marker = '-';\n            }\n            if (preg_match('/rgb\\(.*?\\)/', $listitemtype, $m)) {\n                $list_item_color = $this->colorConverter->convert($m[0], $this->PDFAXwarnings);\n            } else {\n                $list_item_color = '';\n            }\n\n            // SAVE then SET COLR\n            $save_colorarray = $this->colorarray;\n            if ($list_item_color) {\n                $this->colorarray = $list_item_color;\n            }\n\n            if ($listitemposition == 'inside') {\n                $e = $list_item_marker . $spacer;\n                $this->_saveTextBuffer($e);\n            } else {\n                $objattr = [];\n                $objattr['type'] = 'listmarker';\n                $objattr['width'] = 0;\n                $objattr['height'] = $this->FontSize;\n                $objattr['vertical-align'] = 'T';\n                $objattr['text'] = $list_item_marker;\n                $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');\n                $objattr['colorarray'] = $this->colorarray;\n                $objattr['fontfamily'] = $this->FontFamily;\n                $objattr['fontsize'] = $this->FontSize;\n                $objattr['fontsizept'] = $this->FontSizePt;\n                $objattr['fontstyle'] = $this->FontStyle;\n                $e = \"\\xbb\\xa4\\xactype=listmarker,objattr=\" . serialize($objattr) . \"\\xbb\\xa4\\xac\";\n                $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array\n            }\n\n            // RESET COLOR\n            $this->colorarray = $save_colorarray;\n\n        } else { // TEXT\n            $counter = $this->listcounter[$this->listlvl];\n\n            if ($listitemtype == 'none') {\n                return;\n            }\n\n            $num = $this->_getStyledNumber($counter, $listitemtype, true);\n\n            if ($listitemposition == 'inside') {\n                $e = $num . $this->list_number_suffix . $spacer;\n                $this->_saveTextBuffer($e);\n            } else {\n                if (isset($this->blk[$this->blklvl]['direction']) && $this->blk[$this->blklvl]['direction'] == 'rtl') {\n                    // REPLACE MIRRORED RTL $this->list_number_suffix  e.g. ) -> (  (NB could use Ucdn::$mirror_pairs)\n                    $m = strtr($this->list_number_suffix, \")]}\", \"([{\") . $num;\n                } else {\n                    $m = $num . $this->list_number_suffix;\n                }\n\n                $objattr = [];\n                $objattr['type'] = 'listmarker';\n                $objattr['width'] = 0;\n                $objattr['height'] = $this->FontSize;\n                $objattr['vertical-align'] = 'T';\n                $objattr['text'] = $m;\n                $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');\n                $objattr['colorarray'] = $this->colorarray;\n                $objattr['fontfamily'] = $this->FontFamily;\n                $objattr['fontsize'] = $this->FontSize;\n                $objattr['fontsizept'] = $this->FontSizePt;\n                $objattr['fontstyle'] = $this->FontStyle;\n                $e = \"\\xbb\\xa4\\xactype=listmarker,objattr=\" . serialize($objattr) . \"\\xbb\\xa4\\xac\";\n\n                $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array\n            }\n        }\n    }\n\n    // mPDF Lists\n    function _getListMarkerWidth(&$currblk, &$a, &$i)\n    {\n        $blt_width = 0;\n\n        $markeroffset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);\n\n        // Get Maximum number in the list\n        $maxnum = $this->listcounter[$this->listlvl];\n        if ($currblk['list_style_type'] != 'disc' && $currblk['list_style_type'] != 'circle' && $currblk['list_style_type'] != 'square') {\n            $lvl = 1;\n            for ($j = $i + 2; $j < count($a); $j+=2) {\n                $e = $a[$j];\n                if (!$e) {\n                    continue;\n                }\n                if ($e[0] == '/') { // end tag\n                    $e = strtoupper(substr($e, 1));\n                    if ($e == 'OL' || $e == 'UL') {\n                        if ($lvl == 1) {\n                            break;\n                        }\n                        $lvl--;\n                    }\n                } else { // opening tag\n                    if (strpos($e, ' ')) {\n                        $e = substr($e, 0, strpos($e, ' '));\n                    }\n                    $e = strtoupper($e);\n                    if ($e == 'LI') {\n                        if ($lvl == 1) {\n                            $maxnum++;\n                        }\n                    } elseif ($e == 'OL' || $e == 'UL') {\n                        $lvl++;\n                    }\n                }\n            }\n        }\n\n        $decToAlpha = new Conversion\\DecToAlpha();\n        $decToRoman = new Conversion\\DecToRoman();\n        $decToOther = new Conversion\\DecToOther($this);\n\n        switch ($currblk['list_style_type']) {\n            case 'decimal':\n            case '1':\n                $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'none':\n                $blt_width = 0;\n                break;\n            case 'upper-alpha':\n            case 'upper-latin':\n            case 'A':\n                $maxnumA = $decToAlpha->convert($maxnum, true);\n                if ($maxnum < 13) {\n                    $blt_width = $this->GetStringWidth('D' . $this->list_number_suffix);\n                } else {\n                    $blt_width = $this->GetStringWidth(str_repeat('W', strlen($maxnumA)) . $this->list_number_suffix);\n                }\n                break;\n            case 'lower-alpha':\n            case 'lower-latin':\n            case 'a':\n                $maxnuma = $decToAlpha->convert($maxnum, false);\n                if ($maxnum < 13) {\n                    $blt_width = $this->GetStringWidth('b' . $this->list_number_suffix);\n                } else {\n                    $blt_width = $this->GetStringWidth(str_repeat('m', strlen($maxnuma)) . $this->list_number_suffix);\n                }\n                break;\n            case 'upper-roman':\n            case 'I':\n                if ($maxnum > 87) {\n                    $bbit = 87;\n                } elseif ($maxnum > 86) {\n                    $bbit = 86;\n                } elseif ($maxnum > 37) {\n                    $bbit = 38;\n                } elseif ($maxnum > 36) {\n                    $bbit = 37;\n                } elseif ($maxnum > 27) {\n                    $bbit = 28;\n                } elseif ($maxnum > 26) {\n                    $bbit = 27;\n                } elseif ($maxnum > 17) {\n                    $bbit = 18;\n                } elseif ($maxnum > 16) {\n                    $bbit = 17;\n                } elseif ($maxnum > 7) {\n                    $bbit = 8;\n                } elseif ($maxnum > 6) {\n                    $bbit = 7;\n                } elseif ($maxnum > 3) {\n                    $bbit = 4;\n                } else {\n                    $bbit = $maxnum;\n                }\n\n                $maxlnum = $decToRoman->convert($bbit, true);\n                $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);\n\n                break;\n            case 'lower-roman':\n            case 'i':\n                if ($maxnum > 87) {\n                    $bbit = 87;\n                } elseif ($maxnum > 86) {\n                    $bbit = 86;\n                } elseif ($maxnum > 37) {\n                    $bbit = 38;\n                } elseif ($maxnum > 36) {\n                    $bbit = 37;\n                } elseif ($maxnum > 27) {\n                    $bbit = 28;\n                } elseif ($maxnum > 26) {\n                    $bbit = 27;\n                } elseif ($maxnum > 17) {\n                    $bbit = 18;\n                } elseif ($maxnum > 16) {\n                    $bbit = 17;\n                } elseif ($maxnum > 7) {\n                    $bbit = 8;\n                } elseif ($maxnum > 6) {\n                    $bbit = 7;\n                } elseif ($maxnum > 3) {\n                    $bbit = 4;\n                } else {\n                    $bbit = $maxnum;\n                }\n                $maxlnum = $decToRoman->convert($bbit, false);\n                $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);\n                break;\n\n            case 'disc':\n            case 'circle':\n            case 'square':\n                $size = $this->sizeConverter->convert($this->list_symbol_size, $this->FontSize);\n                $offset = $this->sizeConverter->convert($this->list_marker_offset, $this->FontSize);\n                $blt_width = $size + $offset;\n                break;\n\n            case 'arabic-indic':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0660), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'persian':\n            case 'urdu':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x06F0), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'bengali':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x09E6), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'devanagari':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0966), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'gujarati':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0AE6), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'gurmukhi':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0A66), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'kannada':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0CE6), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'malayalam':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(6, 0x0D66), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'oriya':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0B66), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'telugu':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(3, 0x0C66), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'tamil':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(9, 0x0BE6), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            case 'thai':\n                $blt_width = $this->GetStringWidth(str_repeat($decToOther->convert(5, 0x0E50), strlen($maxnum)) . $this->list_number_suffix);\n                break;\n            default:\n                $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);\n                break;\n        }\n\n        return ($blt_width + $markeroffset);\n    }\n\n    function _saveTextBuffer($t, $link = '', $intlink = '', $return = false)\n    {\n        // mPDF 6  Lists\n        $arr = [];\n        $arr[0] = $t;\n        if (isset($link) && $link) {\n            $arr[1] = $link;\n        }\n        $arr[2] = $this->currentfontstyle;\n        if (isset($this->colorarray) && $this->colorarray) {\n            $arr[3] = $this->colorarray;\n        }\n        $arr[4] = $this->currentfontfamily;\n        $arr[5] = $this->currentLang; // mPDF 6\n        if (isset($intlink) && $intlink) {\n            $arr[7] = $intlink;\n        }\n        // mPDF 6\n        // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,\n        // set for kerning via kern table\n        // e.g. Latin script when useOTL set as 0x80\n        if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {\n            $this->textvar = ($this->textvar | TextVars::FC_KERNING);\n        }\n        $arr[8] = $this->textvar; // mPDF 5.7.1\n        if (isset($this->textparam) && $this->textparam) {\n            $arr[9] = $this->textparam;\n        }\n        if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {\n            $arr[10] = $this->spanbgcolorarray;\n        }\n        $arr[11] = $this->currentfontsize;\n        if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {\n            $arr[12] = $this->ReqFontStyle;\n        }\n        if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {\n            $arr[14] = $this->lSpacingCSS;\n        }\n        if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {\n            $arr[15] = $this->wSpacingCSS;\n        }\n        if (isset($this->spanborddet) && $this->spanborddet) {\n            $arr[16] = $this->spanborddet;\n        }\n        if (isset($this->textshadow) && $this->textshadow) {\n            $arr[17] = $this->textshadow;\n        }\n        if (isset($this->OTLdata) && $this->OTLdata) {\n            $arr[18] = $this->OTLdata;\n            $this->OTLdata = [];\n        } // mPDF 5.7.1\n        else {\n            $arr[18] = null;\n        }\n        // mPDF 6  Lists\n        if ($return) {\n            return ($arr);\n        }\n        if ($this->listitem) {\n            $this->textbuffer[] = $this->listitem;\n            $this->listitem = [];\n        }\n        $this->textbuffer[] = $arr;\n    }\n\n    function _saveCellTextBuffer($t, $link = '', $intlink = '')\n    {\n        $arr = [];\n        $arr[0] = $t;\n        if (isset($link) && $link) {\n            $arr[1] = $link;\n        }\n        $arr[2] = $this->currentfontstyle;\n        if (isset($this->colorarray) && $this->colorarray) {\n            $arr[3] = $this->colorarray;\n        }\n        $arr[4] = $this->currentfontfamily;\n        if (isset($intlink) && $intlink) {\n            $arr[7] = $intlink;\n        }\n        // mPDF 6\n        // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,\n        // set for kerning via kern table\n        // e.g. Latin script when useOTL set as 0x80\n        if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {\n            $this->textvar = ($this->textvar | TextVars::FC_KERNING);\n        }\n        $arr[8] = $this->textvar; // mPDF 5.7.1\n        if (isset($this->textparam) && $this->textparam) {\n            $arr[9] = $this->textparam;\n        }\n        if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray) {\n            $arr[10] = $this->spanbgcolorarray;\n        }\n        $arr[11] = $this->currentfontsize;\n        if (isset($this->ReqFontStyle) && $this->ReqFontStyle) {\n            $arr[12] = $this->ReqFontStyle;\n        }\n        if (isset($this->lSpacingCSS) && $this->lSpacingCSS) {\n            $arr[14] = $this->lSpacingCSS;\n        }\n        if (isset($this->wSpacingCSS) && $this->wSpacingCSS) {\n            $arr[15] = $this->wSpacingCSS;\n        }\n        if (isset($this->spanborddet) && $this->spanborddet) {\n            $arr[16] = $this->spanborddet;\n        }\n        if (isset($this->textshadow) && $this->textshadow) {\n            $arr[17] = $this->textshadow;\n        }\n        if (isset($this->OTLdata) && $this->OTLdata) {\n            $arr[18] = $this->OTLdata;\n            $this->OTLdata = [];\n        } // mPDF 5.7.1\n        else {\n            $arr[18] = null;\n        }\n        $this->cell[$this->row][$this->col]['textbuffer'][] = $arr;\n    }\n\n    function printbuffer($arrayaux, $blockstate = 0, $is_table = false, $table_draft = false, $cell_dir = '')\n    {\n        // $blockstate = 0;\t// NO margins/padding\n        // $blockstate = 1;\t// Top margins/padding only\n        // $blockstate = 2;\t// Bottom margins/padding only\n        // $blockstate = 3;\t// Top & bottom margins/padding\n        $this->spanbgcolorarray = '';\n        $this->spanbgcolor = false;\n        $this->spanborder = false;\n        $this->spanborddet = [];\n        $paint_ht_corr = 0;\n        /* -- CSS-FLOAT -- */\n        if (count($this->floatDivs)) {\n            [$l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width] = $this->GetFloatDivInfo($this->blklvl);\n            if (($this->blk[$this->blklvl]['inner_width'] - $l_width - $r_width) < (2 * $this->GetCharWidth('W', false))) {\n                // Too narrow to fit - try to move down past L or R float\n                if ($l_max < $r_max && ($this->blk[$this->blklvl]['inner_width'] - $r_width) > (2 * $this->GetCharWidth('W', false))) {\n                    $this->ClearFloats('LEFT', $this->blklvl);\n                } elseif ($r_max < $l_max && ($this->blk[$this->blklvl]['inner_width'] - $l_width) > (2 * $this->GetCharWidth('W', false))) {\n                    $this->ClearFloats('RIGHT', $this->blklvl);\n                } else {\n                    $this->ClearFloats('BOTH', $this->blklvl);\n                }\n            }\n        }\n        /* -- END CSS-FLOAT -- */\n        $bak_y = $this->y;\n        $bak_x = $this->x;\n        $align = '';\n        if (!$is_table) {\n            if (isset($this->blk[$this->blklvl]['align']) && $this->blk[$this->blklvl]['align']) {\n                $align = $this->blk[$this->blklvl]['align'];\n            }\n            // Block-align is set by e.g. <.. align=\"center\"> Takes priority for this block but not inherited\n            if (isset($this->blk[$this->blklvl]['block-align']) && $this->blk[$this->blklvl]['block-align']) {\n                $align = $this->blk[$this->blklvl]['block-align'];\n            }\n            if (isset($this->blk[$this->blklvl]['direction'])) {\n                $blockdir = $this->blk[$this->blklvl]['direction'];\n            } else {\n                $blockdir = \"\";\n            }\n            $this->divwidth = $this->blk[$this->blklvl]['width'];\n        } else {\n            $align = $this->cellTextAlign;\n            $blockdir = $cell_dir;\n        }\n        $oldpage = $this->page;\n\n        // ADDED for Out of Block now done as Flowing Block\n        if ($this->divwidth == 0) {\n            $this->divwidth = $this->pgwidth;\n        }\n\n        if (!$is_table) {\n            $this->SetLineHeight($this->FontSizePt, $this->blk[$this->blklvl]['line_height']);\n        }\n        $this->divheight = $this->lineheight;\n        $old_height = $this->divheight;\n\n        // As a failsafe - if font has been set but not output to page\n        if (!$table_draft) {\n            $this->SetFont($this->default_font, '', $this->default_font_size, true, true); // force output to page\n        }\n\n        $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, true, $blockdir, $table_draft);\n\n        $array_size = count($arrayaux);\n\n        // Added - Otherwise <div><div><p> did not output top margins/padding for 1st/2nd div\n        if ($array_size == 0) {\n            $this->finishFlowingBlock(true);\n        } // true = END of flowing block\n        // mPDF 6\n        // ALL the chunks of textbuffer need to have at least basic OTLdata set\n        // First make sure each element/chunk has the OTLdata for Bidi set.\n        for ($i = 0; $i < $array_size; $i++) {\n            if (empty($arrayaux[$i][18])) {\n                if (substr($arrayaux[$i][0], 0, 3) == \"\\xbb\\xa4\\xac\") { // object identifier has been identified!\n                    $unicode = [0xFFFC]; // Object replacement character\n                } else {\n                    $unicode = $this->UTF8StringToArray($arrayaux[$i][0], false);\n                }\n                $is_strong = false;\n                $this->getBasicOTLdata($arrayaux[$i][18], $unicode, $is_strong);\n            }\n            // Gets messed up if try and use core fonts inside a paragraph of text which needs to be BiDi re-ordered or OTLdata set\n            if (($blockdir == 'rtl' || $this->biDirectional) && isset($arrayaux[$i][4]) && in_array($arrayaux[$i][4], ['ccourier', 'ctimes', 'chelvetica', 'csymbol', 'czapfdingbats'])) {\n                throw new \\Mpdf\\MpdfException(\"You cannot use core fonts in a document which contains RTL text.\");\n            }\n        }\n        // mPDF 6\n        // Process bidirectional text ready for bidi-re-ordering (which is done after line-breaks are established in WriteFlowingBlock etc.)\n        if (($blockdir == 'rtl' || $this->biDirectional) && !$table_draft) {\n            if (empty($this->otl)) {\n                $this->otl = new Otl($this, $this->fontCache);\n            }\n            $this->otl->bidiPrepare($arrayaux, $blockdir);\n            $array_size = count($arrayaux);\n        }\n\n\n        // Remove empty items // mPDF 6\n        for ($i = $array_size - 1; $i > 0; $i--) {\n            if (empty($arrayaux[$i][0]) && (isset($arrayaux[$i][16]) && $arrayaux[$i][16] !== '0') && empty($arrayaux[$i][7])) {\n                unset($arrayaux[$i]);\n            }\n        }\n\n        // Correct adjoining borders for inline elements\n        if (isset($arrayaux[0][16])) {\n            $lastspanborder = $arrayaux[0][16];\n        } else {\n            $lastspanborder = false;\n        }\n        for ($i = 1; $i < $array_size; $i++) {\n            if (isset($arrayaux[$i][16]) && $arrayaux[$i][16] == $lastspanborder &&\n                ((!isset($arrayaux[$i][9]['bord-decoration']) && !isset($arrayaux[$i - 1][9]['bord-decoration'])) ||\n                    (isset($arrayaux[$i][9]['bord-decoration']) && isset($arrayaux[$i - 1][9]['bord-decoration']) && $arrayaux[$i][9]['bord-decoration'] == $arrayaux[$i - 1][9]['bord-decoration'])\n                )\n            ) {\n                if (isset($arrayaux[$i][16]['R'])) {\n                    $lastspanborder = $arrayaux[$i][16];\n                } else {\n                    $lastspanborder = false;\n                }\n                $arrayaux[$i][16]['L']['s'] = 0;\n                $arrayaux[$i][16]['L']['w'] = 0;\n                $arrayaux[$i - 1][16]['R']['s'] = 0;\n                $arrayaux[$i - 1][16]['R']['w'] = 0;\n            } else {\n                if (isset($arrayaux[$i][16]['R'])) {\n                    $lastspanborder = $arrayaux[$i][16];\n                } else {\n                    $lastspanborder = false;\n                }\n            }\n        }\n\n        for ($i = 0; $i < $array_size; $i++) {\n            // COLS\n            $oldcolumn = $this->CurrCol;\n            $vetor = isset($arrayaux[$i]) ? $arrayaux[$i] : null;\n            if ($i == 0 && $vetor[0] != \"\\n\" && ! $this->ispre) {\n                $vetor[0] = ltrim($vetor[0]);\n                if (!empty($vetor[18])) {\n                    $this->otl->trimOTLdata($vetor[18], true, false);\n                } // *OTL*\n            }\n\n            // FIXED TO ALLOW IT TO SHOW '0'\n            if (empty($vetor[0]) && !($vetor[0] === '0') && empty($vetor[7])) { // Ignore empty text and not carrying an internal link\n                // Check if it is the last element. If so then finish printing the block\n                if ($i == ($array_size - 1)) {\n                    $this->finishFlowingBlock(true);\n                } // true = END of flowing block\n                continue;\n            }\n\n\n            // Activating buffer properties\n            if (isset($vetor[11]) && $vetor[11] != '') {   // Font Size\n                if ($is_table && $this->shrin_k) {\n                    $this->SetFontSize($vetor[11] / $this->shrin_k, false);\n                } else {\n                    $this->SetFontSize($vetor[11], false);\n                }\n            }\n\n            if (isset($vetor[17]) && !empty($vetor[17])) { // TextShadow\n                $this->textshadow = $vetor[17];\n            }\n            if (isset($vetor[16]) && !empty($vetor[16])) { // Border\n                $this->spanborddet = $vetor[16];\n                $this->spanborder = true;\n            }\n\n            if (isset($vetor[15])) {   // Word spacing\n                $this->wSpacingCSS = $vetor[15];\n                if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {\n                    $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3\n                }\n            }\n            if (isset($vetor[14])) {   // Letter spacing\n                $this->lSpacingCSS = $vetor[14];\n                if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {\n                    $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3\n                }\n            }\n\n\n            if (isset($vetor[10]) and ! empty($vetor[10])) { // Background color\n                $this->spanbgcolorarray = $vetor[10];\n                $this->spanbgcolor = true;\n            }\n            if (isset($vetor[9]) and ! empty($vetor[9])) { // Text parameters - Outline + hyphens\n                $this->textparam = $vetor[9];\n                $this->SetTextOutline($this->textparam);\n                // mPDF 5.7.3  inline text-decoration parameters\n                if ($is_table && $this->shrin_k) {\n                    if (isset($this->textparam['text-baseline'])) {\n                        $this->textparam['text-baseline'] /= $this->shrin_k;\n                    }\n                    if (isset($this->textparam['decoration-baseline'])) {\n                        $this->textparam['decoration-baseline'] /= $this->shrin_k;\n                    }\n                    if (isset($this->textparam['decoration-fontsize'])) {\n                        $this->textparam['decoration-fontsize'] /= $this->shrin_k;\n                    }\n                }\n            }\n            if (isset($vetor[8])) {  // mPDF 5.7.1\n                $this->textvar = $vetor[8];\n            }\n            if (isset($vetor[7]) and $vetor[7] != '') { // internal target: <a name=\"anyvalue\">\n                $ily = $this->y;\n                if ($this->table_rotate) {\n                    $this->internallink[$vetor[7]] = [\"Y\" => $ily, \"PAGE\" => $this->page, \"tbrot\" => true];\n                } elseif ($this->kwt) {\n                    $this->internallink[$vetor[7]] = [\"Y\" => $ily, \"PAGE\" => $this->page, \"kwt\" => true];\n                } elseif ($this->ColActive) {\n                    $this->internallink[$vetor[7]] = [\"Y\" => $ily, \"PAGE\" => $this->page, \"col\" => $this->CurrCol];\n                } elseif (!$this->keep_block_together) {\n                    $this->internallink[$vetor[7]] = [\"Y\" => $ily, \"PAGE\" => $this->page];\n                }\n                if (empty($vetor[0])) { // Ignore empty text\n                    // Check if it is the last element. If so then finish printing the block\n                    if ($i == ($array_size - 1)) {\n                        $this->finishFlowingBlock(true);\n                    } // true = END of flowing block\n                    continue;\n                }\n            }\n            if (isset($vetor[5]) and $vetor[5] != '') {  // Language\t// mPDF 6\n                $this->currentLang = $vetor[5];\n            }\n            if (isset($vetor[4]) and $vetor[4] != '') {  // Font Family\n                $font = $this->SetFont($vetor[4], $this->FontStyle, 0, false);\n            }\n            if (!empty($vetor[3])) { // Font Color\n                $cor = $vetor[3];\n                $this->SetTColor($cor);\n            }\n            if (isset($vetor[2]) and $vetor[2] != '') { // Bold,Italic styles\n                $this->SetStyles($vetor[2]);\n            }\n\n            if (isset($vetor[12]) and $vetor[12] != '') { // Requested Bold,Italic\n                $this->ReqFontStyle = $vetor[12];\n            }\n            if (isset($vetor[1]) and $vetor[1] != '') { // LINK\n                if (strpos($vetor[1], \".\") === false && strpos($vetor[1], \"@\") !== 0) { // assuming every external link has a dot indicating extension (e.g: .html .txt .zip www.somewhere.com etc.)\n                    // Repeated reference to same anchor?\n                    while (array_key_exists($vetor[1], $this->internallink)) {\n                        $vetor[1] = \"#\" . $vetor[1];\n                    }\n                    $this->internallink[$vetor[1]] = $this->AddLink();\n                    $vetor[1] = $this->internallink[$vetor[1]];\n                }\n                $this->HREF = $vetor[1];     // HREF link style set here ******\n            }\n\n            // SPECIAL CONTENT - IMAGES & FORM OBJECTS\n            // Print-out special content\n\n            if (substr($vetor[0], 0, 3) == \"\\xbb\\xa4\\xac\") { // identifier has been identified!\n                $objattr = $this->_getObjAttr($vetor[0]);\n\n                /* -- TABLES -- */\n                if ($objattr['type'] == 'nestedtable') {\n                    if ($objattr['nestedcontent']) {\n                        $level = $objattr['level'];\n                        $table = &$this->table[$level][$objattr['table']];\n\n                        if ($table_draft) {\n                            $this->y += $this->table[($level + 1)][$objattr['nestedcontent']]['h']; // nested table height\n                            $this->finishFlowingBlock(false, 'nestedtable');\n                        } else {\n                            $cell = &$table['cells'][$objattr['row']][$objattr['col']];\n                            $this->finishFlowingBlock(false, 'nestedtable');\n                            $save_dw = $this->divwidth;\n                            $save_buffer = $this->cellBorderBuffer;\n                            $this->cellBorderBuffer = [];\n                            $ncx = $this->x;\n                            [$dummyx, $w] = $this->_tableGetWidth($table, $objattr['row'], $objattr['col']);\n                            $ntw = $this->table[($level + 1)][$objattr['nestedcontent']]['w']; // nested table width\n                            if (!$this->simpleTables) {\n                                if ($this->packTableData) {\n                                    [$bt, $br, $bb, $bl] = $this->_getBorderWidths($cell['borderbin']);\n                                } else {\n                                    $br = $cell['border_details']['R']['w'];\n                                    $bl = $cell['border_details']['L']['w'];\n                                }\n                                if ($table['borders_separate']) {\n                                    $innerw = $w - $bl - $br - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];\n                                } else {\n                                    $innerw = $w - $bl / 2 - $br / 2 - $cell['padding']['L'] - $cell['padding']['R'];\n                                }\n                            } elseif ($this->simpleTables) {\n                                if ($table['borders_separate']) {\n                                    $innerw = $w - $table['simple']['border_details']['L']['w'] - $table['simple']['border_details']['R']['w'] - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];\n                                } else {\n                                    $innerw = $w - $table['simple']['border_details']['L']['w'] / 2 - $table['simple']['border_details']['R']['w'] / 2 - $cell['padding']['L'] - $cell['padding']['R'];\n                                }\n                            }\n                            if ($cell['a'] == 'C' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'C') {\n                                $ncx += ($innerw - $ntw) / 2;\n                            } elseif ($cell['a'] == 'R' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'R') {\n                                $ncx += $innerw - $ntw;\n                            }\n                            $this->x = $ncx;\n\n                            $this->_tableWrite($this->table[($level + 1)][$objattr['nestedcontent']]);\n                            $this->cellBorderBuffer = $save_buffer;\n                            $this->x = $bak_x;\n                            $this->divwidth = $save_dw;\n                        }\n\n                        $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);\n                    }\n                } else {\n                    /* -- END TABLES -- */\n                    if ($is_table) { // *TABLES*\n                        $maxWidth = $this->divwidth;  // *TABLES*\n                    } // *TABLES*\n                    else { // *TABLES*\n                        $maxWidth = $this->divwidth - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w']);\n                    } // *TABLES*\n\n                    /* -- CSS-IMAGE-FLOAT -- */\n                    // If float (already) exists at this level\n                    if (isset($this->floatmargins['R']) && $this->y <= $this->floatmargins['R']['y1'] && $this->y >= $this->floatmargins['R']['y0']) {\n                        $maxWidth -= $this->floatmargins['R']['w'];\n                    }\n                    if (isset($this->floatmargins['L']) && $this->y <= $this->floatmargins['L']['y1'] && $this->y >= $this->floatmargins['L']['y0']) {\n                        $maxWidth -= $this->floatmargins['L']['w'];\n                    }\n                    /* -- END CSS-IMAGE-FLOAT -- */\n\n                    [$skipln] = $this->inlineObject($objattr['type'], '', $this->y, $objattr, $this->lMargin, ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE), $maxWidth, $this->flowingBlockAttr['height'], false, $is_table);\n                    //  1 -> New line needed because of width\n                    // -1 -> Will fit width on line but NEW PAGE REQUIRED because of height\n                    // -2 -> Will not fit on line therefore needs new line but thus NEW PAGE REQUIRED\n                    $iby = $this->y;\n                    $oldpage = $this->page;\n                    $oldcol = $this->CurrCol;\n                    if (($skipln == 1 || $skipln == -2) && !isset($objattr['float'])) {\n                        $this->finishFlowingBlock(false, $objattr['type']);\n                        $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);\n                    }\n\n                    if (!$table_draft) {\n                        $thispage = $this->page;\n                        if ($this->CurrCol != $oldcol) {\n                            $changedcol = true;\n                        } else {\n                            $changedcol = false;\n                        }\n\n                        // the previous lines can already have triggered page break or column change\n                        if (!$changedcol && $skipln < 0 && $this->AcceptPageBreak() && $thispage == $oldpage) {\n                            $this->AddPage($this->CurOrientation);\n\n                            // Added to correct Images already set on line before page advanced\n                            // i.e. if second inline image on line is higher than first and forces new page\n                            if (count($this->objectbuffer)) {\n                                $yadj = $iby - $this->y;\n                                foreach ($this->objectbuffer as $ib => $val) {\n                                    if ($this->objectbuffer[$ib]['OUTER-Y']) {\n                                        $this->objectbuffer[$ib]['OUTER-Y'] -= $yadj;\n                                    }\n                                    if ($this->objectbuffer[$ib]['BORDER-Y']) {\n                                        $this->objectbuffer[$ib]['BORDER-Y'] -= $yadj;\n                                    }\n                                    if ($this->objectbuffer[$ib]['INNER-Y']) {\n                                        $this->objectbuffer[$ib]['INNER-Y'] -= $yadj;\n                                    }\n                                }\n                            }\n                        }\n\n                        // Added to correct for OddEven Margins\n                        if ($this->page != $oldpage) {\n                            if (($this->page - $oldpage) % 2 == 1) {\n                                $bak_x += $this->MarginCorrection;\n                            }\n                            $oldpage = $this->page;\n                            $y = $this->tMargin - $paint_ht_corr;\n                            $this->oldy = $this->tMargin - $paint_ht_corr;\n                            $old_height = 0;\n                        }\n                        $this->x = $bak_x;\n                        /* -- COLUMNS -- */\n                        // COLS\n                        // OR COLUMN CHANGE\n                        if ($this->CurrCol != $oldcolumn) {\n                            if ($this->directionality == 'rtl') { // *OTL*\n                                $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*\n                            } // *OTL*\n                            else { // *OTL*\n                                $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);\n                            } // *OTL*\n                            $this->x = $bak_x;\n                            $oldcolumn = $this->CurrCol;\n                            $y = $this->y0 - $paint_ht_corr;\n                            $this->oldy = $this->y0 - $paint_ht_corr;\n                            $old_height = 0;\n                        }\n                        /* -- END COLUMNS -- */\n                    }\n\n                    /* -- CSS-IMAGE-FLOAT -- */\n                    if ($objattr['type'] == 'image' && isset($objattr['float'])) {\n                        $fy = $this->y;\n\n                        // DIV TOP MARGIN/BORDER/PADDING\n                        if ($this->flowingBlockAttr['newblock'] && ($this->flowingBlockAttr['blockstate'] == 1 || $this->flowingBlockAttr['blockstate'] == 3) && $this->flowingBlockAttr['lineCount'] == 0) {\n                            $fy += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];\n                        }\n\n                        if ($objattr['float'] == 'R') {\n                            $fx = $this->w - $this->rMargin - $objattr['width'] - ($this->blk[$this->blklvl]['outer_right_margin'] + $this->blk[$this->blklvl]['border_right']['w'] + $this->blk[$this->blklvl]['padding_right']);\n                        } elseif ($objattr['float'] == 'L') {\n                            $fx = $this->lMargin + ($this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left']);\n                        }\n                        $w = $objattr['width'];\n                        $h = abs($objattr['height']);\n\n                        $widthLeft = $maxWidth - ($this->flowingBlockAttr['contentWidth'] / Mpdf::SCALE);\n                        $maxHeight = $this->h - ($this->tMargin + $this->margin_header + $this->bMargin + 10);\n                        // For Images\n                        $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']);\n                        $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']);\n\n                        if ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg') {\n                            $file = $objattr['file'];\n                            $info = $this->formobjects[$file];\n                        } else {\n                            $file = $objattr['file'];\n                            $info = $this->images[$file];\n                        }\n                        $img_w = $w - $extraWidth;\n                        $img_h = $h - $extraHeight;\n                        if ($objattr['border_left']['w']) {\n                            $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] + $objattr['border_right']['w']) / 2);\n                            $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] + $objattr['border_bottom']['w']) / 2);\n                            $objattr['BORDER-X'] = $fx + $objattr['margin_left'] + (($objattr['border_left']['w']) / 2);\n                            $objattr['BORDER-Y'] = $fy + $objattr['margin_top'] + (($objattr['border_top']['w']) / 2);\n                        }\n                        $objattr['INNER-WIDTH'] = $img_w;\n                        $objattr['INNER-HEIGHT'] = $img_h;\n                        $objattr['INNER-X'] = $fx + $objattr['margin_left'] + ($objattr['border_left']['w']);\n                        $objattr['INNER-Y'] = $fy + $objattr['margin_top'] + ($objattr['border_top']['w']);\n                        $objattr['ID'] = $info['i'];\n                        $objattr['OUTER-WIDTH'] = $w;\n                        $objattr['OUTER-HEIGHT'] = $h;\n                        $objattr['OUTER-X'] = $fx;\n                        $objattr['OUTER-Y'] = $fy;\n                        if ($objattr['float'] == 'R') {\n                            // If R float already exists at this level\n                            $this->floatmargins['R']['skipline'] = false;\n                            if (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {\n                                $this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1\n                            } // If L float already exists at this level\n                            elseif (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {\n                                // Final check distance between floats is not now too narrow to fit text\n                                $mw = 2 * $this->GetCharWidth('W', false);\n                                if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['L']['w']) < $mw) {\n                                    $this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1\n                                } else {\n                                    $this->floatmargins['R']['x'] = $fx;\n                                    $this->floatmargins['R']['w'] = $w;\n                                    $this->floatmargins['R']['y0'] = $fy;\n                                    $this->floatmargins['R']['y1'] = $fy + $h;\n                                    if ($skipln == 1) {\n                                        $this->floatmargins['R']['skipline'] = true;\n                                        $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;\n                                        $objattr['skipline'] = true;\n                                    }\n                                    $this->floatbuffer[] = $objattr;\n                                }\n                            } else {\n                                $this->floatmargins['R']['x'] = $fx;\n                                $this->floatmargins['R']['w'] = $w;\n                                $this->floatmargins['R']['y0'] = $fy;\n                                $this->floatmargins['R']['y1'] = $fy + $h;\n                                if ($skipln == 1) {\n                                    $this->floatmargins['R']['skipline'] = true;\n                                    $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;\n                                    $objattr['skipline'] = true;\n                                }\n                                $this->floatbuffer[] = $objattr;\n                            }\n                        } elseif ($objattr['float'] == 'L') {\n                            // If L float already exists at this level\n                            $this->floatmargins['L']['skipline'] = false;\n                            if (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {\n                                $this->floatmargins['L']['skipline'] = false;\n                                $this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1\n                            } // If R float already exists at this level\n                            elseif (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {\n                                // Final check distance between floats is not now too narrow to fit text\n                                $mw = 2 * $this->GetCharWidth('W', false);\n                                if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['R']['w']) < $mw) {\n                                    $this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1\n                                } else {\n                                    $this->floatmargins['L']['x'] = $fx + $w;\n                                    $this->floatmargins['L']['w'] = $w;\n                                    $this->floatmargins['L']['y0'] = $fy;\n                                    $this->floatmargins['L']['y1'] = $fy + $h;\n                                    if ($skipln == 1) {\n                                        $this->floatmargins['L']['skipline'] = true;\n                                        $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;\n                                        $objattr['skipline'] = true;\n                                    }\n                                    $this->floatbuffer[] = $objattr;\n                                }\n                            } else {\n                                $this->floatmargins['L']['x'] = $fx + $w;\n                                $this->floatmargins['L']['w'] = $w;\n                                $this->floatmargins['L']['y0'] = $fy;\n                                $this->floatmargins['L']['y1'] = $fy + $h;\n                                if ($skipln == 1) {\n                                    $this->floatmargins['L']['skipline'] = true;\n                                    $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;\n                                    $objattr['skipline'] = true;\n                                }\n                                $this->floatbuffer[] = $objattr;\n                            }\n                        }\n                    } else {\n                        /* -- END CSS-IMAGE-FLOAT -- */\n                        $this->WriteFlowingBlock($vetor[0], (isset($vetor[18]) ? $vetor[18] : null));  // mPDF 5.7.1\n                        /* -- CSS-IMAGE-FLOAT -- */\n                    }\n                    /* -- END CSS-IMAGE-FLOAT -- */\n                } // *TABLES*\n            } // END If special content\n            else { // THE text\n                if ($this->tableLevel) {\n                    $paint_ht_corr = 0;\n                } // To move the y up when new column/page started if div border needed\n                else {\n                    $paint_ht_corr = $this->blk[$this->blklvl]['border_top']['w'];\n                }\n\n                if ($vetor[0] == \"\\n\") { // We are reading a <BR> now turned into newline (\"\\n\")\n                    if ($this->flowingBlockAttr['content']) {\n                        $this->finishFlowingBlock(false, 'br');\n                    } elseif ($is_table) {\n                        $this->y+= $this->_computeLineheight($this->cellLineHeight);\n                    } elseif (!$is_table) {\n                        $this->DivLn($this->lineheight);\n                        if ($this->ColActive) {\n                            $this->breakpoints[$this->CurrCol][] = $this->y;\n                        } // *COLUMNS*\n                    }\n                    // Added to correct for OddEven Margins\n                    if ($this->page != $oldpage) {\n                        if (($this->page - $oldpage) % 2 == 1) {\n                            $bak_x += $this->MarginCorrection;\n                        }\n                        $oldpage = $this->page;\n                        $y = $this->tMargin - $paint_ht_corr;\n                        $this->oldy = $this->tMargin - $paint_ht_corr;\n                        $old_height = 0;\n                    }\n                    $this->x = $bak_x;\n                    /* -- COLUMNS -- */\n                    // COLS\n                    // OR COLUMN CHANGE\n                    if ($this->CurrCol != $oldcolumn) {\n                        if ($this->directionality == 'rtl') { // *OTL*\n                            $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*\n                        } // *OTL*\n                        else { // *OTL*\n                            $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);\n                        } // *OTL*\n                        $this->x = $bak_x;\n                        $oldcolumn = $this->CurrCol;\n                        $y = $this->y0 - $paint_ht_corr;\n                        $this->oldy = $this->y0 - $paint_ht_corr;\n                        $old_height = 0;\n                    }\n                    /* -- END COLUMNS -- */\n                    $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);\n                } else {\n                    $this->WriteFlowingBlock($vetor[0], $vetor[18]);  // mPDF 5.7.1\n                    // Added to correct for OddEven Margins\n                    if ($this->page != $oldpage) {\n                        if (($this->page - $oldpage) % 2 == 1) {\n                            $bak_x += $this->MarginCorrection;\n                            $this->x = $bak_x;\n                        }\n                        $oldpage = $this->page;\n                        $y = $this->tMargin - $paint_ht_corr;\n                        $this->oldy = $this->tMargin - $paint_ht_corr;\n                        $old_height = 0;\n                    }\n                    /* -- COLUMNS -- */\n                    // COLS\n                    // OR COLUMN CHANGE\n                    if ($this->CurrCol != $oldcolumn) {\n                        if ($this->directionality == 'rtl') { // *OTL*\n                            $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*\n                        } // *OTL*\n                        else { // *OTL*\n                            $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);\n                        } // *OTL*\n                        $this->x = $bak_x;\n                        $oldcolumn = $this->CurrCol;\n                        $y = $this->y0 - $paint_ht_corr;\n                        $this->oldy = $this->y0 - $paint_ht_corr;\n                        $old_height = 0;\n                    }\n                    /* -- END COLUMNS -- */\n                }\n            }\n\n            // Check if it is the last element. If so then finish printing the block\n            if ($i == ($array_size - 1)) {\n                $this->finishFlowingBlock(true); // true = END of flowing block\n                // Added to correct for OddEven Margins\n                if ($this->page != $oldpage) {\n                    if (($this->page - $oldpage) % 2 == 1) {\n                        $bak_x += $this->MarginCorrection;\n                        $this->x = $bak_x;\n                    }\n                    $oldpage = $this->page;\n                    $y = $this->tMargin - $paint_ht_corr;\n                    $this->oldy = $this->tMargin - $paint_ht_corr;\n                    $old_height = 0;\n                }\n\n                /* -- COLUMNS -- */\n                // COLS\n                // OR COLUMN CHANGE\n                if ($this->CurrCol != $oldcolumn) {\n                    if ($this->directionality == 'rtl') { // *OTL*\n                        $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*\n                    } // *OTL*\n                    else { // *OTL*\n                        $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);\n                    } // *OTL*\n                    $this->x = $bak_x;\n                    $oldcolumn = $this->CurrCol;\n                    $y = $this->y0 - $paint_ht_corr;\n                    $this->oldy = $this->y0 - $paint_ht_corr;\n                    $old_height = 0;\n                }\n                /* -- END COLUMNS -- */\n            }\n\n            // RESETTING VALUES\n            $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n            $this->colorarray = '';\n            $this->spanbgcolorarray = '';\n            $this->spanbgcolor = false;\n            $this->spanborder = false;\n            $this->spanborddet = [];\n            $this->HREF = '';\n            $this->textparam = [];\n            $this->SetTextOutline();\n\n            $this->textvar = 0x00; // mPDF 5.7.1\n            $this->OTLtags = [];\n            $this->textshadow = '';\n\n            $this->currentfontfamily = '';\n            $this->currentfontsize = '';\n            $this->currentfontstyle = '';\n            $this->currentLang = $this->default_lang;  // mPDF 6\n            $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6\n            /* -- TABLES -- */\n            if ($this->tableLevel) {\n                $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*\n            } else { \t\t\t/* -- END TABLES -- */\n                if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {\n                    $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height\n                }\n            }\n            $this->ResetStyles();\n            $this->lSpacingCSS = '';\n            $this->wSpacingCSS = '';\n            $this->fixedlSpacing = false;\n            $this->minwSpacing = 0;\n            $this->SetDash();\n            $this->dash_on = false;\n            $this->dotted_on = false;\n        }//end of for(i=0;i<arraysize;i++)\n\n        $this->Reset(); // mPDF 6\n        // PAINT DIV BORDER\t// DISABLED IN COLUMNS AS DOESN'T WORK WHEN BROKEN ACROSS COLS??\n        if ((isset($this->blk[$this->blklvl]['border']) || isset($this->blk[$this->blklvl]['bgcolor']) || isset($this->blk[$this->blklvl]['box_shadow'])) && $blockstate && ($this->y != $this->oldy)) {\n            $bottom_y = $this->y; // Does not include Bottom Margin\n            if (isset($this->blk[$this->blklvl]['startpage']) && $this->blk[$this->blklvl]['startpage'] != $this->page && $blockstate != 1) {\n                $this->PaintDivBB('pagetop', $blockstate);\n            } elseif ($blockstate != 1) {\n                $this->PaintDivBB('', $blockstate);\n            }\n            $this->y = $bottom_y;\n            $this->x = $bak_x;\n        }\n\n        // Reset Font\n        $this->SetFontSize($this->default_font_size, false);\n        if ($table_draft) {\n            $ch = $this->y - $bak_y;\n            $this->y = $bak_y;\n            $this->x = $bak_x;\n            return $ch;\n        }\n    }\n\n    function _setDashBorder($style, $div, $cp, $side)\n    {\n        if ($style == 'dashed' && (($side == 'L' || $side == 'R') || ($side == 'T' && $div != 'pagetop' && !$cp) || ($side == 'B' && $div != 'pagebottom') )) {\n            $dashsize = 2; // final dash will be this + 1*linewidth\n            $dashsizek = 1.5; // ratio of Dash/Blank\n            $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));\n        } elseif ($style == 'dotted' || ($side == 'T' && ($div == 'pagetop' || $cp)) || ($side == 'B' && $div == 'pagebottom')) {\n            // Round join and cap\n            $this->SetLineJoin(1);\n            $this->SetLineCap(1);\n            $this->SetDash(0.001, ($this->LineWidth * 3));\n        }\n    }\n\n    function _setBorderLine($b, $k = 1)\n    {\n        $this->SetLineWidth($b['w'] / $k);\n        $this->SetDColor($b['c']);\n        if ($b['c'][0] == 5) { // RGBa\n            $this->SetAlpha(ord($b['c'][4]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2\n        } elseif ($b['c'][0] == 6) { // CMYKa\n            $this->SetAlpha(ord($b['c'][5]) / 100, 'Normal', false, 'S'); // mPDF 5.7.2\n        }\n    }\n\n    function PaintDivBB($divider = '', $blockstate = 0, $blvl = 0)\n    {\n        // Borders & backgrounds are done elsewhere for columns - messes up the repositioning in printcolumnbuffer\n        if ($this->ColActive) {\n            return;\n        } // *COLUMNS*\n        if ($this->keep_block_together) {\n            return;\n        } // mPDF 6\n        $save_y = $this->y;\n        if (!$blvl) {\n            $blvl = $this->blklvl;\n        }\n        $x0 = $x1 = $y0 = $y1 = 0;\n\n        // Added mPDF 3.0 Float DIV\n        if (isset($this->blk[$blvl]['bb_painted'][$this->page]) && $this->blk[$blvl]['bb_painted'][$this->page]) {\n            return;\n        } // *CSS-FLOAT*\n\n        if (isset($this->blk[$blvl]['x0'])) {\n            $x0 = $this->blk[$blvl]['x0'];\n        } // left\n        if (isset($this->blk[$blvl]['y1'])) {\n            $y1 = $this->blk[$blvl]['y1'];\n        } // bottom\n        // Added mPDF 3.0 Float DIV - ensures backgrounds/borders are drawn to bottom of page\n        if ($y1 == 0) {\n            if ($divider == 'pagebottom') {\n                $y1 = $this->h - $this->bMargin;\n            } else {\n                $y1 = $this->y;\n            }\n        }\n\n        $continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page);\n\n        if (isset($this->blk[$blvl]['y0'])) {\n            $y0 = $this->blk[$blvl]['y0'];\n        }\n        $h = $y1 - $y0;\n        $w = $this->blk[$blvl]['width'];\n        $x1 = $x0 + $w;\n\n        // Set border-widths as used here\n        $border_top = $this->blk[$blvl]['border_top']['w'];\n        $border_bottom = $this->blk[$blvl]['border_bottom']['w'];\n        $border_left = $this->blk[$blvl]['border_left']['w'];\n        $border_right = $this->blk[$blvl]['border_right']['w'];\n        if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {\n            $border_top = 0;\n        }\n        if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {\n            $border_bottom = 0;\n        }\n\n        $brTL_H = 0;\n        $brTL_V = 0;\n        $brTR_H = 0;\n        $brTR_V = 0;\n        $brBL_H = 0;\n        $brBL_V = 0;\n        $brBR_H = 0;\n        $brBR_V = 0;\n\n        $brset = false;\n        /* -- BORDER-RADIUS -- */\n        if (isset($this->blk[$blvl]['border_radius_TL_H'])) {\n            $brTL_H = $this->blk[$blvl]['border_radius_TL_H'];\n            $brset = true;\n        }\n        if (isset($this->blk[$blvl]['border_radius_TL_V'])) {\n            $brTL_V = $this->blk[$blvl]['border_radius_TL_V'];\n            $brset = true;\n        }\n        if (isset($this->blk[$blvl]['border_radius_TR_H'])) {\n            $brTR_H = $this->blk[$blvl]['border_radius_TR_H'];\n            $brset = true;\n        }\n        if (isset($this->blk[$blvl]['border_radius_TR_V'])) {\n            $brTR_V = $this->blk[$blvl]['border_radius_TR_V'];\n            $brset = true;\n        }\n        if (isset($this->blk[$blvl]['border_radius_BR_H'])) {\n            $brBR_H = $this->blk[$blvl]['border_radius_BR_H'];\n            $brset = true;\n        }\n        if (isset($this->blk[$blvl]['border_radius_BR_V'])) {\n            $brBR_V = $this->blk[$blvl]['border_radius_BR_V'];\n            $brset = true;\n        }\n        if (isset($this->blk[$blvl]['border_radius_BL_H'])) {\n            $brBL_H = $this->blk[$blvl]['border_radius_BL_H'];\n            $brset = true;\n        }\n        if (isset($this->blk[$blvl]['border_radius_BL_V'])) {\n            $brBL_V = $this->blk[$blvl]['border_radius_BL_V'];\n            $brset = true;\n        }\n\n        if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {\n            $brTL_H = 0;\n            $brTL_V = 0;\n            $brTR_H = 0;\n            $brTR_V = 0;\n        }\n        if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {\n            $brBL_H = 0;\n            $brBL_V = 0;\n            $brBR_H = 0;\n            $brBR_V = 0;\n        }\n\n        // Disallow border-radius if it is smaller than the border width.\n        if ($brTL_H < min($border_left, $border_top)) {\n            $brTL_H = $brTL_V = 0;\n        }\n        if ($brTL_V < min($border_left, $border_top)) {\n            $brTL_V = $brTL_H = 0;\n        }\n        if ($brTR_H < min($border_right, $border_top)) {\n            $brTR_H = $brTR_V = 0;\n        }\n        if ($brTR_V < min($border_right, $border_top)) {\n            $brTR_V = $brTR_H = 0;\n        }\n        if ($brBL_H < min($border_left, $border_bottom)) {\n            $brBL_H = $brBL_V = 0;\n        }\n        if ($brBL_V < min($border_left, $border_bottom)) {\n            $brBL_V = $brBL_H = 0;\n        }\n        if ($brBR_H < min($border_right, $border_bottom)) {\n            $brBR_H = $brBR_V = 0;\n        }\n        if ($brBR_V < min($border_right, $border_bottom)) {\n            $brBR_V = $brBR_H = 0;\n        }\n\n        // CHECK FOR radii that sum to > width or height of div ********\n        $f = min($h / ($brTL_V + $brBL_V + 0.001), $h / ($brTR_V + $brBR_V + 0.001), $w / ($brTL_H + $brTR_H + 0.001), $w / ($brBL_H + $brBR_H + 0.001));\n        if ($f < 1) {\n            $brTL_H *= $f;\n            $brTL_V *= $f;\n            $brTR_H *= $f;\n            $brTR_V *= $f;\n            $brBL_H *= $f;\n            $brBL_V *= $f;\n            $brBR_H *= $f;\n            $brBR_V *= $f;\n        }\n        /* -- END BORDER-RADIUS -- */\n\n        $tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);\n        for ($l = 0; $l <= $blvl; $l++) {\n            if ($this->blk[$l]['bgcolor']) {\n                $tbcol = $this->blk[$l]['bgcolorarray'];\n            }\n        }\n\n        // BORDERS\n        if (isset($this->blk[$blvl]['y0']) && $this->blk[$blvl]['y0']) {\n            $y0 = $this->blk[$blvl]['y0'];\n        }\n        $h = $y1 - $y0;\n        $w = $this->blk[$blvl]['width'];\n\n        if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {\n            $tbd = $this->blk[$blvl]['border_top'];\n\n            $legend = '';\n            $legbreakL = 0;\n            $legbreakR = 0;\n            // BORDER LEGEND\n            if (isset($this->blk[$blvl]['border_legend']) && $this->blk[$blvl]['border_legend']) {\n                $legend = $this->blk[$blvl]['border_legend']; // Same structure array as textbuffer\n                $txt = $legend[0] = ltrim($legend[0]);\n                if (!empty($legend[18])) {\n                    $this->otl->trimOTLdata($legend[18], true, false);\n                } // *OTL*\n                // Set font, size, style, color\n                $this->SetFont($legend[4], $legend[2], $legend[11]);\n                if (isset($legend[3]) && $legend[3]) {\n                    $cor = $legend[3];\n                    $this->SetTColor($cor);\n                }\n                $stringWidth = $this->GetStringWidth($txt, true, $legend[18], $legend[8]);\n                $save_x = $this->x;\n                $save_y = $this->y;\n                $save_currentfontfamily = $this->FontFamily;\n                $save_currentfontsize = $this->FontSizePt;\n                $save_currentfontstyle = $this->FontStyle;\n                $this->y = $y0 - $this->FontSize / 2 + $this->blk[$blvl]['border_top']['w'] / 2;\n                $this->x = $x0 + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_left']['w'];\n\n                // Set the distance from the border line to the text ? make configurable variable\n                $gap = 0.2 * $this->FontSize;\n                $legbreakL = $this->x - $gap;\n                $legbreakR = $this->x + $stringWidth + $gap;\n                $this->magic_reverse_dir($txt, $this->blk[$blvl]['direction'], $legend[18]);\n                $fill = '';\n                $this->Cell($stringWidth, $this->FontSize, $txt, '', 0, 'C', $fill, '', 0, 0, 0, 'M', $fill, false, $legend[18], $legend[8]);\n                // Reset\n                $this->x = $save_x;\n                $this->y = $save_y;\n                $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);\n                $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            }\n\n            if (isset($tbd['s']) && $tbd['s']) {\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('q');\n                    $this->SetLineWidth(0);\n                    $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));\n                    $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path\n                }\n\n                $this->_setBorderLine($tbd);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $legbreakL -= $border_top / 2; // because line cap different\n                    $legbreakR += $border_top / 2;\n                    $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'T');\n                } /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brTR_V && $brTR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                }\n                $s = '';\n                if ($brTR_H && $brTR_V) {\n                    $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_top / 2, $brTR_V - $border_top / 2, 1, 2, true)) . \"\\n\";\n                } else { \t\t\t\t/* -- END BORDER-RADIUS -- */\n                    if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                }\n                /* -- BORDER-RADIUS -- */\n                if ($brTL_H && $brTL_V) {\n                    if ($legend) {\n                        if ($legbreakR < ($x0 + $w - $brTR_H)) {\n                            $s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                        }\n                        if ($legbreakL > ($x0 + $brTL_H )) {\n                            $s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                            $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE) . \"\\n\");\n                        } else {\n                            $s .= (sprintf('%.3F %.3F m ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                        }\n                    } else {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                    $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_top / 2, $brTL_V - $border_top / 2, 2, 1)) . \"\\n\";\n                } else {\n                    /* -- END BORDER-RADIUS -- */\n                    if ($legend) {\n                        if ($legbreakR < ($x0 + $w)) {\n                            $s .= (sprintf('%.3F %.3F l ', $legbreakR * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                        }\n                        if ($legbreakL > ($x0)) {\n                            $s .= (sprintf('%.3F %.3F m ', $legbreakL * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                            if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                                $s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                            } else {\n                                $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                            }\n                        } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                            $s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                        } else {\n                            $s .= (sprintf('%.3F %.3F m ', ($x0 + $border_top / 2) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                        }\n                    } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_top / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                    /* -- BORDER-RADIUS -- */\n                }\n                /* -- END BORDER-RADIUS -- */\n                $s .= 'S' . \"\\n\";\n                $this->writer->write($s);\n\n                if ($tbd['style'] == 'double') {\n                    $this->SetLineWidth($tbd['w'] / 3);\n                    $this->SetDColor($tbcol);\n                    $this->writer->write($s);\n                }\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('Q');\n                }\n\n                // Reset Corners and Dash off\n                $this->SetLineWidth(0.1);\n                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        // Reinstate line above for dotted line divider when block border crosses a page\n        // elseif ($divider == 'pagetop' || $continuingpage) {\n\n        if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {\n            $tbd = $this->blk[$blvl]['border_bottom'];\n            if (isset($tbd['s']) && $tbd['s']) {\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('q');\n                    $this->SetLineWidth(0);\n                    $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));\n                    $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path\n                }\n\n                $this->_setBorderLine($tbd);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'B');\n                } /* -- BORDER-RADIUS -- */ elseif (($brBL_V && $brBL_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                }\n                $s = '';\n                if ($brBL_H && $brBL_V) {\n                    $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_bottom / 2, $brBL_V - $border_bottom / 2, 3, 2, true)) . \"\\n\";\n                } else { \t\t\t\t/* -- END BORDER-RADIUS -- */\n                    if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                }\n                /* -- BORDER-RADIUS -- */\n                if ($brBR_H && $brBR_V) {\n                    $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2) - $brBR_H ) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    $s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_bottom / 2, $brBR_V - $border_bottom / 2, 4, 1)) . \"\\n\";\n                } else { \t\t\t\t/* -- END BORDER-RADIUS -- */\n                    if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_bottom / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                }\n                $s .= 'S' . \"\\n\";\n                $this->writer->write($s);\n\n                if ($tbd['style'] == 'double') {\n                    $this->SetLineWidth($tbd['w'] / 3);\n                    $this->SetDColor($tbcol);\n                    $this->writer->write($s);\n                }\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('Q');\n                }\n\n                // Reset Corners and Dash off\n                $this->SetLineWidth(0.1);\n                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        // Reinstate line below for dotted line divider when block border crosses a page\n        // elseif ($blockstate == 1 || $divider == 'pagebottom') {\n\n        if ($this->blk[$blvl]['border_left']) {\n            $tbd = $this->blk[$blvl]['border_left'];\n            if (isset($tbd['s']) && $tbd['s']) {\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('q');\n                    $this->SetLineWidth(0);\n                    $this->writer->write(sprintf('%.3F %.3F m ', ($x0) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $border_left) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));\n                    $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path\n                }\n\n                $this->_setBorderLine($tbd);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'L');\n                } /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brBL_V && $brBL_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                }\n                $s = '';\n                if ($brTL_V && $brTL_H) {\n                    $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_left / 2, $brTL_V - $border_left / 2, 2, 2, true)) . \"\\n\";\n                } else { \t\t\t\t/* -- END BORDER-RADIUS -- */\n                    if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_left / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                }\n                /* -- BORDER-RADIUS -- */\n                if ($brBL_V && $brBL_H) {\n                    $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2) - $brBL_V) ) * Mpdf::SCALE)) . \"\\n\";\n                    $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_left / 2, $brBL_V - $border_left / 2, 3, 1)) . \"\\n\";\n                } else { \t\t\t\t/* -- END BORDER-RADIUS -- */\n                    if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h) ) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_left / 2)) ) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                }\n                $s .= 'S' . \"\\n\";\n                $this->writer->write($s);\n\n                if ($tbd['style'] == 'double') {\n                    $this->SetLineWidth($tbd['w'] / 3);\n                    $this->SetDColor($tbcol);\n                    $this->writer->write($s);\n                }\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('Q');\n                }\n\n                // Reset Corners and Dash off\n                $this->SetLineWidth(0.1);\n                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        if ($this->blk[$blvl]['border_right']) {\n            $tbd = $this->blk[$blvl]['border_right'];\n            if (isset($tbd['s']) && $tbd['s']) {\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('q');\n                    $this->SetLineWidth(0);\n                    $this->writer->write(sprintf('%.3F %.3F m ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $border_top)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * Mpdf::SCALE, ($this->h - ($y0 + $h - $border_bottom)) * Mpdf::SCALE));\n                    $this->writer->write(sprintf('%.3F %.3F l ', ($x0 + $w) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE));\n                    $this->writer->write(' h W n '); // Ends path no-op & Sets the clipping path\n                }\n\n                $this->_setBorderLine($tbd);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'R');\n                } /* -- BORDER-RADIUS -- */ elseif (($brTR_V && $brTR_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                }\n                $s = '';\n                if ($brBR_V && $brBR_H) {\n                    $s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_right / 2, $brBR_V - $border_right / 2, 4, 2, true)) . \"\\n\";\n                } else { \t\t\t\t/* -- END BORDER-RADIUS -- */\n                    if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h)) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + $h - ($border_right / 2))) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                }\n                /* -- BORDER-RADIUS -- */\n                if ($brTR_V && $brTR_H) {\n                    $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2) + $brTR_V) ) * Mpdf::SCALE)) . \"\\n\";\n                    $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_right / 2, $brTR_V - $border_right / 2, 1, 1)) . \"\\n\";\n                } else { \t\t\t\t/* -- END BORDER-RADIUS -- */\n                    if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0) ) * Mpdf::SCALE)) . \"\\n\";\n                    } else {\n                        $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * Mpdf::SCALE, ($this->h - ($y0 + ($border_right / 2)) ) * Mpdf::SCALE)) . \"\\n\";\n                    }\n                }\n                $s .= 'S' . \"\\n\";\n                $this->writer->write($s);\n\n                if ($tbd['style'] == 'double') {\n                    $this->SetLineWidth($tbd['w'] / 3);\n                    $this->SetDColor($tbcol);\n                    $this->writer->write($s);\n                }\n                if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {\n                    $this->writer->write('Q');\n                }\n\n                // Reset Corners and Dash off\n                $this->SetLineWidth(0.1);\n                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n\n\n        $this->SetDash();\n        $this->y = $save_y;\n\n\n        // BACKGROUNDS are disabled in columns/kbt/headers - messes up the repositioning in printcolumnbuffer\n        if ($this->ColActive || $this->kwt || $this->keep_block_together) {\n            return;\n        }\n\n\n        $bgx0 = $x0;\n        $bgx1 = $x1;\n        $bgy0 = $y0;\n        $bgy1 = $y1;\n\n        // Defined br values represent the radius of the outer curve - need to take border-width/2 from each radius for drawing the borders\n        if (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'padding-box') {\n            $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w']);\n            $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w']);\n            $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w']);\n            $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w']);\n            $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w']);\n            $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w']);\n            $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w']);\n            $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w']);\n            $bgx0 += $this->blk[$blvl]['border_left']['w'];\n            $bgx1 -= $this->blk[$blvl]['border_right']['w'];\n            if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {\n                $bgy0 += $this->blk[$blvl]['border_top']['w'];\n            }\n            if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {\n                $bgy1 -= $this->blk[$blvl]['border_bottom']['w'];\n            }\n        } elseif (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'content-box') {\n            $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);\n            $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);\n            $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);\n            $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);\n            $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);\n            $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);\n            $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);\n            $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);\n            $bgx0 += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];\n            $bgx1 -= $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right'];\n            if (($this->blk[$blvl]['border_top']['w'] || $this->blk[$blvl]['padding_top']) && $divider != 'pagetop' && !$continuingpage) {\n                $bgy0 += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];\n            }\n            if (($this->blk[$blvl]['border_bottom']['w'] || $this->blk[$blvl]['padding_bottom']) && $blockstate != 1 && $divider != 'pagebottom') {\n                $bgy1 -= $this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom'];\n            }\n        } else {\n            $brbgTL_H = $brTL_H;\n            $brbgTL_V = $brTL_V;\n            $brbgTR_H = $brTR_H;\n            $brbgTR_V = $brTR_V;\n            $brbgBL_H = $brBL_H;\n            $brbgBL_V = $brBL_V;\n            $brbgBR_H = $brBR_H;\n            $brbgBR_V = $brBR_V;\n        }\n\n        // Set clipping path\n        $s = ' q 0 w '; // Line width=0\n        $s .= sprintf('%.3F %.3F m ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // start point TL before the arc\n        /* -- BORDER-RADIUS -- */\n        if ($brbgTL_H || $brbgTL_V) {\n            $s .= $this->_EllipseArc($bgx0 + $brbgTL_H, $bgy0 + $brbgTL_V, $brbgTL_H, $brbgTL_V, 2); // segment 2 TL\n        }\n        /* -- END BORDER-RADIUS -- */\n        $s .= sprintf('%.3F %.3F l ', ($bgx0) * Mpdf::SCALE, ($this->h - ($bgy1 - $brbgBL_V )) * Mpdf::SCALE); // line to BL\n        /* -- BORDER-RADIUS -- */\n        if ($brbgBL_H || $brbgBL_V) {\n            $s .= $this->_EllipseArc($bgx0 + $brbgBL_H, $bgy1 - $brbgBL_V, $brbgBL_H, $brbgBL_V, 3); // segment 3 BL\n        }\n        /* -- END BORDER-RADIUS -- */\n        $s .= sprintf('%.3F %.3F l ', ($bgx1 - $brbgBR_H ) * Mpdf::SCALE, ($this->h - ($bgy1)) * Mpdf::SCALE); // line to BR\n        /* -- BORDER-RADIUS -- */\n        if ($brbgBR_H || $brbgBR_V) {\n            $s .= $this->_EllipseArc($bgx1 - $brbgBR_H, $bgy1 - $brbgBR_V, $brbgBR_H, $brbgBR_V, 4); // segment 4 BR\n        }\n        /* -- END BORDER-RADIUS -- */\n        $s .= sprintf('%.3F %.3F l ', ($bgx1) * Mpdf::SCALE, ($this->h - ($bgy0 + $brbgTR_V)) * Mpdf::SCALE); // line to TR\n        /* -- BORDER-RADIUS -- */\n        if ($brbgTR_H || $brbgTR_V) {\n            $s .= $this->_EllipseArc($bgx1 - $brbgTR_H, $bgy0 + $brbgTR_V, $brbgTR_H, $brbgTR_V, 1); // segment 1 TR\n        }\n        /* -- END BORDER-RADIUS -- */\n        $s .= sprintf('%.3F %.3F l ', ($bgx0 + $brbgTL_H ) * Mpdf::SCALE, ($this->h - $bgy0) * Mpdf::SCALE); // line to TL\n        // Box Shadow\n        $shadow = '';\n        if (isset($this->blk[$blvl]['box_shadow']) && $this->blk[$blvl]['box_shadow'] && $h > 0) {\n            foreach ($this->blk[$blvl]['box_shadow'] as $sh) {\n                // Colors\n                if ($sh['col'][0] == 1) {\n                    $colspace = 'Gray';\n                    if ($sh['col'][2] == 1) {\n                        $col1 = '1' . $sh['col'][1] . '1' . $sh['col'][3];\n                    } else {\n                        $col1 = '1' . $sh['col'][1] . '1' . chr(100);\n                    }\n                    $col2 = '1' . $sh['col'][1] . '1' . chr(0);\n                } elseif ($sh['col'][0] == 4) { // CMYK\n                    $colspace = 'CMYK';\n                    $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(100);\n                    $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);\n                } elseif ($sh['col'][0] == 5) { // RGBa\n                    $colspace = 'RGB';\n                    $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4];\n                    $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);\n                } elseif ($sh['col'][0] == 6) { // CMYKa\n                    $colspace = 'CMYK';\n                    $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . $sh['col'][5];\n                    $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);\n                } else {\n                    $colspace = 'RGB';\n                    $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(100);\n                    $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);\n                }\n\n                // Use clipping path as set above (and rectangle around page) to clip area outside box\n                $shadow .= $s; // Use the clipping path with W*\n                $shadow .= sprintf('0 %.3F m %.3F %.3F l ', $this->h * Mpdf::SCALE, $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);\n                $shadow .= sprintf('%.3F 0 l 0 0 l 0 %.3F l ', $this->w * Mpdf::SCALE, $this->h * Mpdf::SCALE);\n                $shadow .= 'W n' . \"\\n\";\n\n                $sh['blur'] = abs($sh['blur']); // cannot have negative blur value\n                // Ensure spread/blur do not make effective shadow width/height < 0\n                // Could do more complex things but this just adjusts spread value\n                if (-$sh['spread'] + $sh['blur'] / 2 > min($w / 2, $h / 2)) {\n                    $sh['spread'] = $sh['blur'] / 2 - min($w / 2, $h / 2) + 0.01;\n                }\n                // Shadow Offset\n                if ($sh['x'] || $sh['y']) {\n                    $shadow .= sprintf(' q 1 0 0 1 %.4F %.4F cm', $sh['x'] * Mpdf::SCALE, -$sh['y'] * Mpdf::SCALE) . \"\\n\";\n                }\n\n                // Set path for INNER shadow\n                $shadow .= ' q 0 w ';\n                $shadow .= $this->SetFColor($col1, true) . \"\\n\";\n                if ($col1[0] == 5 && ord($col1[4]) < 100) { // RGBa\n                    $shadow .= $this->SetAlpha(ord($col1[4]) / 100, 'Normal', true, 'F') . \"\\n\";\n                } elseif ($col1[0] == 6 && ord($col1[5]) < 100) { // CMYKa\n                    $shadow .= $this->SetAlpha(ord($col1[5]) / 100, 'Normal', true, 'F') . \"\\n\";\n                } elseif ($col1[0] == 1 && $col1[2] == 1 && ord($col1[3]) < 100) { // Gray\n                    $shadow .= $this->SetAlpha(ord($col1[3]) / 100, 'Normal', true, 'F') . \"\\n\";\n                }\n\n                // Blur edges\n                $mag = 0.551784; // Bezier Control magic number for 4-part spline for circle/ellipse\n                $mag2 = 0.551784; // Bezier Control magic number to fill in edge of blurred rectangle\n                $d1 = $sh['spread'] + $sh['blur'] / 2;\n                $d2 = $sh['spread'] - $sh['blur'] / 2;\n                $bl = $sh['blur'];\n                $x00 = $x0 - $d1;\n                $y00 = $y0 - $d1;\n                $w00 = $w + $d1 * 2;\n                $h00 = $h + $d1 * 2;\n\n                // If any border-radius is greater width-negative spread(inner edge), ignore radii for shadow or screws up\n                $flatten = false;\n                if (max($brbgTR_H, $brbgTL_H, $brbgBR_H, $brbgBL_H) >= $w + $d2) {\n                    $flatten = true;\n                }\n                if (max($brbgTR_V, $brbgTL_V, $brbgBR_V, $brbgBL_V) >= $h + $d2) {\n                    $flatten = true;\n                }\n\n\n                // TOP RIGHT corner\n                $p1x = $x00 + $w00 - $d1 - $brbgTR_H;\n                $p1c2x = $p1x + ($d2 + $brbgTR_H) * $mag;\n                $p1y = $y00 + $bl;\n                $p2x = $x00 + $w00 - $d1 - $brbgTR_H;\n                $p2c2x = $p2x + ($d1 + $brbgTR_H) * $mag;\n                $p2y = $y00;\n                $p2c1y = $p2y + $bl / 2;\n                $p3x = $x00 + $w00;\n                $p3c2x = $p3x - $bl / 2;\n                $p3y = $y00 + $d1 + $brbgTR_V;\n                $p3c1y = $p3y - ($d1 + $brbgTR_V) * $mag;\n                $p4x = $x00 + $w00 - $bl;\n                $p4y = $y00 + $d1 + $brbgTR_V;\n                $p4c2y = $p4y - ($d2 + $brbgTR_V) * $mag;\n                if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {\n                    $p1x = $x00 + $w00 - $bl;\n                    $p1c2x = $p1x;\n                    $p2x = $x00 + $w00 - $bl;\n                    $p2c2x = $p2x + $bl * $mag2;\n                    $p3y = $y00 + $bl;\n                    $p3c1y = $p3y - $bl * $mag2;\n                    $p4y = $y00 + $bl;\n                    $p4c2y = $p4y;\n                }\n\n                $shadow .= sprintf('%.3F %.3F m ', ($p1x ) * Mpdf::SCALE, ($this->h - ($p1y )) * Mpdf::SCALE);\n                $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * Mpdf::SCALE, ($this->h - ($p1y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4c2y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);\n                $patch_array[0]['f'] = 0;\n                $patch_array[0]['points'] = [$p1x, $p1y, $p1x, $p1y,\n                    $p2x, $p2c1y, $p2x, $p2y, $p2c2x, $p2y,\n                    $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,\n                    $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,\n                    $p1c2x, $p1y];\n                $patch_array[0]['colors'] = [$col1, $col2, $col2, $col1];\n\n\n                // RIGHT\n                $p1x = $x00 + $w00; // control point only matches p3 preceding\n                $p1y = $y00 + $d1 + $brbgTR_V;\n                $p2x = $x00 + $w00 - $bl; // control point only matches p4 preceding\n                $p2y = $y00 + $d1 + $brbgTR_V;\n                $p3x = $x00 + $w00 - $bl;\n                $p3y = $y00 + $h00 - $d1 - $brbgBR_V;\n                $p4x = $x00 + $w00;\n                $p4c1x = $p4x - $bl / 2;\n                $p4y = $y00 + $h00 - $d1 - $brbgBR_V;\n                if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {\n                    $p1y = $y00 + $bl;\n                    $p2y = $y00 + $bl;\n                }\n                if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {\n                    $p3y = $y00 + $h00 - $bl;\n                    $p4y = $y00 + $h00 - $bl;\n                }\n\n                $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);\n                $patch_array[1]['f'] = 2;\n                $patch_array[1]['points'] = [$p2x, $p2y,\n                    $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,\n                    $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,\n                    $p1x, $p1y];\n                $patch_array[1]['colors'] = [$col1, $col2];\n\n\n                // BOTTOM RIGHT corner\n                $p1x = $x00 + $w00 - $bl;  // control points only matches p3 preceding\n                $p1y = $y00 + $h00 - $d1 - $brbgBR_V;\n                $p1c2y = $p1y + ($d2 + $brbgBR_V) * $mag;\n                $p2x = $x00 + $w00;     // control point only matches p4 preceding\n                $p2y = $y00 + $h00 - $d1 - $brbgBR_V;\n                $p2c2y = $p2y + ($d1 + $brbgBR_V) * $mag;\n                $p3x = $x00 + $w00 - $d1 - $brbgBR_H;\n                $p3c1x = $p3x + ($d1 + $brbgBR_H) * $mag;\n                $p3y = $y00 + $h00;\n                $p3c2y = $p3y - $bl / 2;\n                $p4x = $x00 + $w00 - $d1 - $brbgBR_H;\n                $p4c2x = $p4x + ($d2 + $brbgBR_H) * $mag;\n                $p4y = $y00 + $h00 - $bl;\n\n                if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {\n                    $p1y = $y00 + $h00 - $bl;\n                    $p1c2y = $p1y;\n                    $p2y = $y00 + $h00 - $bl;\n                    $p2c2y = $p2y + $bl * $mag2;\n                    $p3x = $x00 + $w00 - $bl;\n                    $p3c1x = $p3x + $bl * $mag2;\n                    $p4x = $x00 + $w00 - $bl;\n                    $p4c2x = $p4x;\n                }\n\n                $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * Mpdf::SCALE, ($this->h - ($p1c2y)) * Mpdf::SCALE, ($p4c2x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);\n                $patch_array[2]['f'] = 2;\n                $patch_array[2]['points'] = [$p2x, $p2c2y,\n                    $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,\n                    $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,\n                    $p1x, $p1c2y];\n                $patch_array[2]['colors'] = [$col2, $col1];\n\n\n\n                // BOTTOM\n                $p1x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p3 preceding\n                $p1y = $y00 + $h00;\n                $p2x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p4 preceding\n                $p2y = $y00 + $h00 - $bl;\n                $p3x = $x00 + $d1 + $brbgBL_H;\n                $p3y = $y00 + $h00 - $bl;\n                $p4x = $x00 + $d1 + $brbgBL_H;\n                $p4y = $y00 + $h00;\n                $p4c1y = $p4y - $bl / 2;\n\n                if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {\n                    $p1x = $x00 + $w00 - $bl;\n                    $p2x = $x00 + $w00 - $bl;\n                }\n                if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {\n                    $p3x = $x00 + $bl;\n                    $p4x = $x00 + $bl;\n                }\n\n                $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);\n                $patch_array[3]['f'] = 2;\n                $patch_array[3]['points'] = [$p2x, $p2y,\n                    $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,\n                    $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,\n                    $p1x, $p1y];\n                $patch_array[3]['colors'] = [$col1, $col2];\n\n                // BOTTOM LEFT corner\n                $p1x = $x00 + $d1 + $brbgBL_H;\n                $p1c2x = $p1x - ($d2 + $brbgBL_H) * $mag; // control points only matches p3 preceding\n                $p1y = $y00 + $h00 - $bl;\n                $p2x = $x00 + $d1 + $brbgBL_H;\n                $p2c2x = $p2x - ($d1 + $brbgBL_H) * $mag; // control point only matches p4 preceding\n                $p2y = $y00 + $h00;\n                $p3x = $x00;\n                $p3c2x = $p3x + $bl / 2;\n                $p3y = $y00 + $h00 - $d1 - $brbgBL_V;\n                $p3c1y = $p3y + ($d1 + $brbgBL_V) * $mag;\n                $p4x = $x00 + $bl;\n                $p4y = $y00 + $h00 - $d1 - $brbgBL_V;\n                $p4c2y = $p4y + ($d2 + $brbgBL_V) * $mag;\n                if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {\n                    $p1x = $x00 + $bl;\n                    $p1c2x = $p1x;\n                    $p2x = $x00 + $bl;\n                    $p2c2x = $p2x - $bl * $mag2;\n                    $p3y = $y00 + $h00 - $bl;\n                    $p3c1y = $p3y + $bl * $mag2;\n                    $p4y = $y00 + $h00 - $bl;\n                    $p4c2y = $p4y;\n                }\n\n                $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * Mpdf::SCALE, ($this->h - ($p1y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4c2y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);\n                $patch_array[4]['f'] = 2;\n                $patch_array[4]['points'] = [$p2c2x, $p2y,\n                    $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,\n                    $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,\n                    $p1c2x, $p1y];\n                $patch_array[4]['colors'] = [$col2, $col1];\n\n\n                // LEFT - joins on the right (C3-C4 of previous): f = 2\n                $p1x = $x00; // control point only matches p3 preceding\n                $p1y = $y00 + $h00 - $d1 - $brbgBL_V;\n                $p2x = $x00 + $bl; // control point only matches p4 preceding\n                $p2y = $y00 + $h00 - $d1 - $brbgBL_V;\n                $p3x = $x00 + $bl;\n                $p3y = $y00 + $d1 + $brbgTL_V;\n                $p4x = $x00;\n                $p4c1x = $p4x + $bl / 2;\n                $p4y = $y00 + $d1 + $brbgTL_V;\n                if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {\n                    $p1y = $y00 + $h00 - $bl;\n                    $p2y = $y00 + $h00 - $bl;\n                }\n                if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {\n                    $p3y = $y00 + $bl;\n                    $p4y = $y00 + $bl;\n                }\n\n                $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);\n                $patch_array[5]['f'] = 2;\n                $patch_array[5]['points'] = [$p2x, $p2y,\n                    $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,\n                    $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,\n                    $p1x, $p1y];\n                $patch_array[5]['colors'] = [$col1, $col2];\n\n                // TOP LEFT corner\n                $p1x = $x00 + $bl;  // control points only matches p3 preceding\n                $p1y = $y00 + $d1 + $brbgTL_V;\n                $p1c2y = $p1y - ($d2 + $brbgTL_V) * $mag;\n                $p2x = $x00;   // control point only matches p4 preceding\n                $p2y = $y00 + $d1 + $brbgTL_V;\n                $p2c2y = $p2y - ($d1 + $brbgTL_V) * $mag;\n                $p3x = $x00 + $d1 + $brbgTL_H;\n                $p3c1x = $p3x - ($d1 + $brbgTL_H) * $mag;\n                $p3y = $y00;\n                $p3c2y = $p3y + $bl / 2;\n                $p4x = $x00 + $d1 + $brbgTL_H;\n                $p4c2x = $p4x - ($d2 + $brbgTL_H) * $mag;\n                $p4y = $y00 + $bl;\n\n                if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {\n                    $p1y = $y00 + $bl;\n                    $p1c2y = $p1y;\n                    $p2y = $y00 + $bl;\n                    $p2c2y = $p2y - $bl * $mag2;\n                    $p3x = $x00 + $bl;\n                    $p3c1x = $p3x - $bl * $mag2;\n                    $p4x = $x00 + $bl;\n                    $p4c2x = $p4x;\n                }\n\n                $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * Mpdf::SCALE, ($this->h - ($p1c2y)) * Mpdf::SCALE, ($p4c2x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE, ($p4x) * Mpdf::SCALE, ($this->h - ($p4y)) * Mpdf::SCALE);\n                $patch_array[6]['f'] = 2;\n                $patch_array[6]['points'] = [$p2x, $p2c2y,\n                    $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,\n                    $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,\n                    $p1x, $p1c2y];\n                $patch_array[6]['colors'] = [$col2, $col1];\n\n\n                // TOP - joins on the right (C3-C4 of previous): f = 2\n                $p1x = $x00 + $d1 + $brbgTL_H; // control point only matches p3 preceding\n                $p1y = $y00;\n                $p2x = $x00 + $d1 + $brbgTL_H; // control point only matches p4 preceding\n                $p2y = $y00 + $bl;\n                $p3x = $x00 + $w00 - $d1 - $brbgTR_H;\n                $p3y = $y00 + $bl;\n                $p4x = $x00 + $w00 - $d1 - $brbgTR_H;\n                $p4y = $y00;\n                $p4c1y = $p4y + $bl / 2;\n                if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {\n                    $p1x = $x00 + $bl;\n                    $p2x = $x00 + $bl;\n                }\n                if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {\n                    $p3x = $x00 + $w00 - $bl;\n                    $p4x = $x00 + $w00 - $bl;\n                }\n\n                $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * Mpdf::SCALE, ($this->h - ($p3y )) * Mpdf::SCALE);\n                $patch_array[7]['f'] = 2;\n                $patch_array[7]['points'] = [$p2x, $p2y,\n                    $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,\n                    $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,\n                    $p1x, $p1y];\n                $patch_array[7]['colors'] = [$col1, $col2];\n\n                $shadow .= ' h f Q ' . \"\\n\"; // Close path and Fill the inner solid shadow\n\n                if ($bl) {\n                    $shadow .= $this->gradient->CoonsPatchMesh($x00, $y00, $w00, $h00, $patch_array, $x00, $x00 + $w00, $y00, $y00 + $h00, $colspace, true);\n                }\n\n                if ($sh['x'] || $sh['y']) {\n                    $shadow .= ' Q' . \"\\n\";  // Shadow Offset\n                }\n                $shadow .= ' Q' . \"\\n\"; // Ends path no-op & Sets the clipping path\n            }\n        }\n\n        $s .= ' W n '; // Ends path no-op & Sets the clipping path\n\n        if ($this->blk[$blvl]['bgcolor']) {\n            $this->pageBackgrounds[$blvl][] = [\n                'x' => $x0,\n                'y' => $y0,\n                'w' => $w,\n                'h' => $h,\n                'col' => $this->blk[$blvl]['bgcolorarray'],\n                'clippath' => $s,\n                'visibility' => $this->visibility,\n                'shadow' => $shadow,\n                'z-index' => $this->current_layer,\n            ];\n        } elseif ($shadow) {\n            $this->pageBackgrounds[$blvl][] = [\n                'x' => 0,\n                'y' => 0,\n                'w' => 0,\n                'h' => 0,\n                'shadowonly' => true,\n                'col' => '',\n                'clippath' => '',\n                'visibility' => $this->visibility,\n                'shadow' => $shadow,\n                'z-index' => $this->current_layer,\n            ];\n        }\n\n        /* -- BACKGROUNDS -- */\n        if (isset($this->blk[$blvl]['gradient'])) {\n            $g = $this->gradient->parseBackgroundGradient($this->blk[$blvl]['gradient']);\n            if ($g) {\n                $gx = $x0;\n                $gy = $y0;\n                $this->pageBackgrounds[$blvl][] = [\n                    'gradient' => true,\n                    'x' => $gx,\n                    'y' => $gy,\n                    'w' => $w,\n                    'h' => $h,\n                    'gradtype' => $g['type'],\n                    'stops' => $g['stops'],\n                    'colorspace' => $g['colorspace'],\n                    'coords' => $g['coords'],\n                    'extend' => $g['extend'],\n                    'clippath' => $s,\n                    'visibility' => $this->visibility,\n                    'z-index' => $this->current_layer\n                ];\n            }\n        }\n\n        if (isset($this->blk[$blvl]['background-image'])) {\n            if (isset($this->blk[$blvl]['background-image']['gradient']) && $this->blk[$blvl]['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->blk[$blvl]['background-image']['gradient'])) {\n                $g = $this->gradient->parseMozGradient($this->blk[$blvl]['background-image']['gradient']);\n                if ($g) {\n                    $gx = $x0;\n                    $gy = $y0;\n                    // origin specifies the background-positioning-area (bpa)\n                    if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {\n                        $gx += $this->blk[$blvl]['border_left']['w'];\n                        $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);\n                        if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {\n                            $gy += $this->blk[$blvl]['border_top']['w'];\n                        }\n                        if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {\n                            $gy1 = $y1 - $this->blk[$blvl]['border_bottom']['w'];\n                        } else {\n                            $gy1 = $y1;\n                        }\n                        $h = $gy1 - $gy;\n                    } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {\n                        $gx += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];\n                        $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);\n                        if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {\n                            $gy += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];\n                        }\n                        if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {\n                            $gy1 = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);\n                        } else {\n                            $gy1 = $y1 - $this->blk[$blvl]['padding_bottom'];\n                        }\n                        $h = $gy1 - $gy;\n                    }\n\n                    if (isset($this->blk[$blvl]['background-image']['size']['w']) && $this->blk[$blvl]['background-image']['size']['w']) {\n                        $size = $this->blk[$blvl]['background-image']['size'];\n                        if ($size['w'] != 'contain' && $size['w'] != 'cover') {\n                            if (stristr($size['w'], '%')) {\n                                $size['w'] = (float) $size['w'];\n                                $size['w'] /= 100;\n                                $w *= $size['w'];\n                            } elseif ($size['w'] != 'auto') {\n                                $w = $size['w'];\n                            }\n                            if (stristr($size['h'], '%')) {\n                                $size['h'] = (float) $size['h'];\n                                $size['h'] /= 100;\n                                $h *= $size['h'];\n                            } elseif ($size['h'] != 'auto') {\n                                $h = $size['h'];\n                            }\n                        }\n                    }\n                    $this->pageBackgrounds[$blvl][] = [\n                        'gradient' => true,\n                        'x' => $gx,\n                        'y' => $gy,\n                        'w' => $w,\n                        'h' => $h,\n                        'gradtype' => $g['type'],\n                        'stops' => $g['stops'],\n                        'colorspace' => $g['colorspace'],\n                        'coords' => $g['coords'],\n                        'extend' => $g['extend'],\n                        'clippath' => $s,\n                        'visibility' => $this->visibility,\n                        'z-index' => $this->current_layer\n                    ];\n                }\n\n            } else {\n\n                $image_id = $this->blk[$blvl]['background-image']['image_id'];\n                $orig_w = $this->blk[$blvl]['background-image']['orig_w'];\n                $orig_h = $this->blk[$blvl]['background-image']['orig_h'];\n                $x_pos = $this->blk[$blvl]['background-image']['x_pos'];\n                $y_pos = $this->blk[$blvl]['background-image']['y_pos'];\n                $x_repeat = $this->blk[$blvl]['background-image']['x_repeat'];\n                $y_repeat = $this->blk[$blvl]['background-image']['y_repeat'];\n                $resize = $this->blk[$blvl]['background-image']['resize'];\n                $opacity = $this->blk[$blvl]['background-image']['opacity'];\n                $itype = $this->blk[$blvl]['background-image']['itype'];\n                $size = $this->blk[$blvl]['background-image']['size'];\n                // origin specifies the background-positioning-area (bpa)\n\n                $bpa = ['x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h];\n\n                if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {\n\n                    $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'];\n                    $bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);\n                    if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {\n                        $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'];\n                    } else {\n                        $bpa['y'] = $y0;\n                    }\n                    if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {\n                        $bpay = $y1 - $this->blk[$blvl]['border_bottom']['w'];\n                    } else {\n                        $bpay = $y1;\n                    }\n                    $bpa['h'] = $bpay - $bpa['y'];\n\n                } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {\n\n                    $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];\n                    $bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);\n                    if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {\n                        $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];\n                    } else {\n                        $bpa['y'] = $y0 + $this->blk[$blvl]['padding_top'];\n                    }\n                    if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {\n                        $bpay = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);\n                    } else {\n                        $bpay = $y1 - $this->blk[$blvl]['padding_bottom'];\n                    }\n                    $bpa['h'] = $bpay - $bpa['y'];\n\n                }\n\n                $this->pageBackgrounds[$blvl][] = [\n                    'x' => $x0,\n                    'y' => $y0,\n                    'w' => $w,\n                    'h' => $h,\n                    'image_id' => $image_id,\n                    'orig_w' => $orig_w,\n                    'orig_h' => $orig_h,\n                    'x_pos' => $x_pos,\n                    'y_pos' => $y_pos,\n                    'x_repeat' => $x_repeat,\n                    'y_repeat' => $y_repeat,\n                    'clippath' => $s,\n                    'resize' => $resize,\n                    'opacity' => $opacity,\n                    'itype' => $itype,\n                    'visibility' => $this->visibility,\n                    'z-index' => $this->current_layer,\n                    'size' => $size,\n                    'bpa' => $bpa\n                ];\n            }\n        }\n        /* -- END BACKGROUNDS -- */\n\n        // Float DIV\n        $this->blk[$blvl]['bb_painted'][$this->page] = true;\n    }\n    /* -- BORDER-RADIUS -- */\n\n    function _EllipseArc($x0, $y0, $rx, $ry, $seg = 1, $part = false, $start = false)\n    {\n        // Anticlockwise segment 1-4 TR-TL-BL-BR (part=1 or 2)\n        $s = '';\n\n        if ($rx < 0) {\n            $rx = 0;\n        }\n\n        if ($ry < 0) {\n            $ry = 0;\n        }\n\n        $rx *= Mpdf::SCALE;\n        $ry *= Mpdf::SCALE;\n\n        $astart = 0;\n\n        if ($seg == 1) { // Top Right\n            $afinish = 90;\n            $nSeg = 4;\n        } elseif ($seg == 2) { // Top Left\n            $afinish = 180;\n            $nSeg = 8;\n        } elseif ($seg == 3) { // Bottom Left\n            $afinish = 270;\n            $nSeg = 12;\n        } else {   // Bottom Right\n            $afinish = 360;\n            $nSeg = 16;\n        }\n\n        $astart = deg2rad((float) $astart);\n        $afinish = deg2rad((float) $afinish);\n\n        $totalAngle = $afinish - $astart;\n        $dt = $totalAngle / $nSeg; // segment angle\n        $dtm = $dt / 3;\n        $x0 *= Mpdf::SCALE;\n        $y0 = ($this->h - $y0) * Mpdf::SCALE;\n        $t1 = $astart;\n        $a0 = $x0 + ($rx * cos($t1));\n        $b0 = $y0 + ($ry * sin($t1));\n        $c0 = -$rx * sin($t1);\n        $d0 = $ry * cos($t1);\n        $op = false;\n\n        for ($i = 1; $i <= $nSeg; $i++) {\n            // Draw this bit of the total curve\n            $t1 = ($i * $dt) + $astart;\n            $a1 = $x0 + ($rx * cos($t1));\n            $b1 = $y0 + ($ry * sin($t1));\n            $c1 = -$rx * sin($t1);\n            $d1 = $ry * cos($t1);\n            if ($i > ($nSeg - 4) && (!$part || ($part == 1 && $i <= $nSeg - 2) || ($part == 2 && $i > $nSeg - 2))) {\n                if ($start && !$op) {\n                    $s .= sprintf('%.3F %.3F m ', $a0, $b0);\n                }\n                $s .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($a0 + ($c0 * $dtm)), ($b0 + ($d0 * $dtm)), ($a1 - ($c1 * $dtm)), ($b1 - ($d1 * $dtm)), $a1, $b1);\n                $op = true;\n            }\n            $a0 = $a1;\n            $b0 = $b1;\n            $c0 = $c1;\n            $d0 = $d1;\n        }\n\n        return $s;\n    }\n\n    /* -- END BORDER-RADIUS -- */\n\n    function PaintDivLnBorder($state = 0, $blvl = 0, $h = 0)\n    {\n        // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom\n        $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;\n\n        $save_y = $this->y;\n\n        $w = $this->blk[$blvl]['width'];\n        $x0 = $this->x;    // left\n        $y0 = $this->y;    // top\n        $x1 = $this->x + $w;   // bottom\n        $y1 = $this->y + $h;   // bottom\n        $continuingpage = (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page);\n\n        if ($this->blk[$blvl]['border_top'] && ($state == 1 || $state == 3)) {\n            $tbd = $this->blk[$blvl]['border_top'];\n            if (isset($tbd['s']) && $tbd['s']) {\n                $this->_setBorderLine($tbd);\n                $this->y = $y0 + ($tbd['w'] / 2);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], '', $continuingpage, 'T');\n                    $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);\n                } else {\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                    $this->Line($x0, $this->y, $x0 + $w, $this->y);\n                }\n                $this->y += $tbd['w'];\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        if ($this->blk[$blvl]['border_left']) {\n            $tbd = $this->blk[$blvl]['border_left'];\n            if (isset($tbd['s']) && $tbd['s']) {\n                $this->_setBorderLine($tbd);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->y = $y0 + ($tbd['w'] / 2);\n                    $this->_setDashBorder($tbd['style'], '', $continuingpage, 'L');\n                    $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));\n                } else {\n                    $this->y = $y0;\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                    $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h);\n                }\n                $this->y += $tbd['w'];\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        if ($this->blk[$blvl]['border_right']) {\n            $tbd = $this->blk[$blvl]['border_right'];\n            if (isset($tbd['s']) && $tbd['s']) {\n                $this->_setBorderLine($tbd);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->y = $y0 + ($tbd['w'] / 2);\n                    $this->_setDashBorder($tbd['style'], '', $continuingpage, 'R');\n                    $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));\n                } else {\n                    $this->y = $y0;\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                    $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h);\n                }\n                $this->y += $tbd['w'];\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        if ($this->blk[$blvl]['border_bottom'] && $state > 1) {\n            $tbd = $this->blk[$blvl]['border_bottom'];\n            if (isset($tbd['s']) && $tbd['s']) {\n                $this->_setBorderLine($tbd);\n                $this->y = $y0 + $h - ($tbd['w'] / 2);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], '', $continuingpage, 'B');\n                    $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);\n                } else {\n                    $this->SetLineJoin(0);\n                    $this->SetLineCap(0);\n                    $this->Line($x0, $this->y, $x0 + $w, $this->y);\n                }\n                $this->y += $tbd['w'];\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        $this->SetDash();\n        $this->y = $save_y;\n    }\n\n    function PaintImgBorder($objattr, $is_table)\n    {\n        // Borders are disabled in columns - messes up the repositioning in printcolumnbuffer\n        if ($this->ColActive) {\n            return;\n        } // *COLUMNS*\n        if ($is_table) {\n            $k = $this->shrin_k;\n        } else {\n            $k = 1;\n        }\n        $h = (isset($objattr['BORDER-HEIGHT']) ? $objattr['BORDER-HEIGHT'] : 0);\n        $w = (isset($objattr['BORDER-WIDTH']) ? $objattr['BORDER-WIDTH'] : 0);\n        $x0 = (isset($objattr['BORDER-X']) ? $objattr['BORDER-X'] : 0);\n        $y0 = (isset($objattr['BORDER-Y']) ? $objattr['BORDER-Y'] : 0);\n\n        // BORDERS\n        if ($objattr['border_top']) {\n            $tbd = $objattr['border_top'];\n            if (!empty($tbd['s'])) {\n                $this->_setBorderLine($tbd, $k);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], '', '', 'T');\n                }\n                $this->Line($x0, $y0, $x0 + $w, $y0);\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        if ($objattr['border_left']) {\n            $tbd = $objattr['border_left'];\n            if (!empty($tbd['s'])) {\n                $this->_setBorderLine($tbd, $k);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], '', '', 'L');\n                }\n                $this->Line($x0, $y0, $x0, $y0 + $h);\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        if ($objattr['border_right']) {\n            $tbd = $objattr['border_right'];\n            if (!empty($tbd['s'])) {\n                $this->_setBorderLine($tbd, $k);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], '', '', 'R');\n                }\n                $this->Line($x0 + $w, $y0, $x0 + $w, $y0 + $h);\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        if ($objattr['border_bottom']) {\n            $tbd = $objattr['border_bottom'];\n            if (!empty($tbd['s'])) {\n                $this->_setBorderLine($tbd, $k);\n                if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {\n                    $this->_setDashBorder($tbd['style'], '', '', 'B');\n                }\n                $this->Line($x0, $y0 + $h, $x0 + $w, $y0 + $h);\n                // Reset Corners and Dash off\n                $this->SetLineJoin(2);\n                $this->SetLineCap(2);\n                $this->SetDash();\n            }\n        }\n        $this->SetDash();\n        $this->SetAlpha(1);\n    }\n\n    /* -- END HTML-CSS -- */\n\n    function Reset()\n    {\n        $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n        $this->SetAlpha(1);\n        $this->colorarray = '';\n\n        $this->spanbgcolorarray = '';\n        $this->spanbgcolor = false;\n        $this->spanborder = false;\n        $this->spanborddet = [];\n\n        $this->ResetStyles();\n\n        $this->HREF = '';\n        $this->textparam = [];\n        $this->SetTextOutline();\n\n        $this->textvar = 0x00; // mPDF 5.7.1\n        $this->OTLtags = [];\n        $this->textshadow = '';\n\n        $this->currentLang = $this->default_lang;  // mPDF 6\n        $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6\n        $this->SetFont($this->default_font, '', 0, false);\n        $this->SetFontSize($this->default_font_size, false);\n\n        $this->currentfontfamily = '';\n        $this->currentfontsize = '';\n        $this->currentfontstyle = '';\n\n        /* -- TABLES -- */\n        if ($this->tableLevel && isset($this->table[1][1]['cellLineHeight'])) {\n            $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*\n        } else { \t\t/* -- END TABLES -- */\n            if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {\n                $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height\n            }\n        }\n\n        $this->lSpacingCSS = '';\n        $this->wSpacingCSS = '';\n        $this->fixedlSpacing = false;\n        $this->minwSpacing = 0;\n        $this->SetDash(); // restore to no dash\n        $this->dash_on = false;\n        $this->dotted_on = false;\n        $this->divwidth = 0;\n        $this->divheight = 0;\n        $this->cellTextAlign = '';\n        $this->cellLineHeight = '';\n        $this->cellLineStackingStrategy = '';\n        $this->cellLineStackingShift = '';\n        $this->oldy = -1;\n\n        $bodystyle = [];\n        if (isset($this->cssManager->CSS['BODY']['FONT-STYLE'])) {\n            $bodystyle['FONT-STYLE'] = $this->cssManager->CSS['BODY']['FONT-STYLE'];\n        }\n        if (isset($this->cssManager->CSS['BODY']['FONT-WEIGHT'])) {\n            $bodystyle['FONT-WEIGHT'] = $this->cssManager->CSS['BODY']['FONT-WEIGHT'];\n        }\n        if (isset($this->cssManager->CSS['BODY']['COLOR'])) {\n            $bodystyle['COLOR'] = $this->cssManager->CSS['BODY']['COLOR'];\n        }\n        if (isset($bodystyle)) {\n            $this->setCSS($bodystyle, 'BLOCK', 'BODY');\n        }\n    }\n\n    /* -- HTML-CSS -- */\n\n    function ReadMetaTags($html)\n    {\n        // changes anykey=anyvalue to anykey=\"anyvalue\" (only do this when this happens inside tags)\n        $regexp = '/ (\\\\w+?)=([^\\\\s>\"]+)/si';\n        $html = preg_replace($regexp, \" \\$1=\\\"\\$2\\\"\", $html);\n        if (preg_match('/<title>(.*?)<\\/title>/si', $html, $m)) {\n            $this->SetTitle($m[1]);\n        }\n        preg_match_all('/<meta [^>]*?(name|content)=\"([^>]*?)\" [^>]*?(name|content)=\"([^>]*?)\".*?>/si', $html, $aux);\n        $firstattr = $aux[1];\n        $secondattr = $aux[3];\n        for ($i = 0; $i < count($aux[0]); $i++) {\n            $name = ( strtoupper($firstattr[$i]) == \"NAME\" ) ? strtoupper($aux[2][$i]) : strtoupper($aux[4][$i]);\n            $content = ( strtoupper($firstattr[$i]) == \"CONTENT\" ) ? $aux[2][$i] : $aux[4][$i];\n            switch ($name) {\n                case \"KEYWORDS\":\n                    $this->SetKeywords($content);\n                    break;\n                case \"AUTHOR\":\n                    $this->SetAuthor($content);\n                    break;\n                case \"DESCRIPTION\":\n                    $this->SetSubject($content);\n                    break;\n            }\n        }\n    }\n\n    function ReadCharset($html)\n    {\n        // Charset conversion\n        if ($this->allow_charset_conversion) {\n            if (preg_match('/<head.*charset=([^\\'\\\"\\s]*).*<\\/head>/si', $html, $m)) {\n                if (strtoupper($m[1]) != 'UTF-8') {\n                    $this->charset_in = strtoupper($m[1]);\n                }\n            }\n        }\n    }\n\n    function setCSS($arrayaux, $type = '', $tag = '')\n    {\n        // type= INLINE | BLOCK | TABLECELL // tag= BODY\n        if (!is_array($arrayaux)) {\n            return; // Removes PHP Warning\n        }\n\n        // mPDF 5.7.3  inline text-decoration parameters\n        $preceeding_fontkey = $this->FontFamily . $this->FontStyle;\n        $preceeding_fontsize = $this->FontSize;\n        $spanbordset = false;\n        $spanbgset = false;\n        // mPDF 6\n        $prevlevel = (($this->blklvl == 0) ? 0 : $this->blklvl - 1);\n\n        // Set font size first so that e.g. MARGIN 0.83em works on font size for this element\n        if (isset($arrayaux['FONT-SIZE'])) {\n            $v = $arrayaux['FONT-SIZE'];\n            if (is_numeric($v[0]) || ($v[0] === '.')) {\n                if ($type == 'BLOCK' && $this->blklvl > 0 && isset($this->blk[$this->blklvl - 1]['InlineProperties']) && isset($this->blk[$this->blklvl - 1]['InlineProperties']['size'])) {\n                    $mmsize = $this->sizeConverter->convert($v, $this->blk[$this->blklvl - 1]['InlineProperties']['size']);\n                } elseif ($type == 'TABLECELL') {\n                    $mmsize = $this->sizeConverter->convert($v, $this->default_font_size / Mpdf::SCALE);\n                } else {\n                    $mmsize = $this->sizeConverter->convert($v, $this->FontSize);\n                }\n                $this->SetFontSize($mmsize * (Mpdf::SCALE), false); // Get size in points (pt)\n            } else {\n                $v = strtoupper($v);\n                if (isset($this->fontsizes[$v])) {\n                    $this->SetFontSize($this->fontsizes[$v] * $this->default_font_size, false);\n                }\n            }\n            if ($tag == 'BODY') {\n                $this->SetDefaultFontSize($this->FontSizePt);\n            }\n        }\n\n        // mPDF 6\n        if (isset($arrayaux['LANG']) && $arrayaux['LANG']) {\n            if ($this->autoLangToFont && !$this->usingCoreFont) {\n                if ($arrayaux['LANG'] != $this->default_lang && $arrayaux['LANG'] != 'UTF-8') {\n                    [$coreSuitable, $mpdf_pdf_unifont] = $this->languageToFont->getLanguageOptions($arrayaux['LANG'], $this->useAdobeCJK);\n                    if ($mpdf_pdf_unifont) {\n                        $arrayaux['FONT-FAMILY'] = $mpdf_pdf_unifont;\n                    }\n                    if ($tag == 'BODY') {\n                        $this->default_lang = $arrayaux['LANG'];\n                    }\n                }\n            }\n            $this->currentLang = $arrayaux['LANG'];\n        }\n\n        // FOR INLINE and BLOCK OR 'BODY'\n        if (isset($arrayaux['FONT-FAMILY'])) {\n            $v = $arrayaux['FONT-FAMILY'];\n            // If it is a font list, get all font types\n            $aux_fontlist = explode(\",\", $v);\n            $found = 0;\n            foreach ($aux_fontlist as $f) {\n                $fonttype = trim($f);\n                $fonttype = preg_replace('/[\"\\']*(.*?)[\"\\']*/', '\\\\1', $fonttype);\n                $fonttype = preg_replace('/ /', '', $fonttype);\n                $v = strtolower(trim($fonttype));\n                if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {\n                    $v = $this->fonttrans[$v];\n                }\n                if ((!$this->onlyCoreFonts && in_array($v, $this->available_unifonts)) ||\n                    in_array($v, ['ccourier', 'ctimes', 'chelvetica']) ||\n                    ($this->onlyCoreFonts && in_array($v, ['courier', 'times', 'helvetica', 'arial'])) ||\n                    in_array($v, ['sjis', 'uhc', 'big5', 'gb'])) {\n                    $fonttype = $v;\n                    $found = 1;\n                    break;\n                }\n            }\n            if (!$found) {\n                foreach ($aux_fontlist as $f) {\n                    $fonttype = trim($f);\n                    $fonttype = preg_replace('/[\"\\']*(.*?)[\"\\']*/', '\\\\1', $fonttype);\n                    $fonttype = preg_replace('/ /', '', $fonttype);\n                    $v = strtolower(trim($fonttype));\n                    if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {\n                        $v = $this->fonttrans[$v];\n                    }\n                    if (in_array($v, $this->sans_fonts) || in_array($v, $this->serif_fonts) || in_array($v, $this->mono_fonts)) {\n                        $fonttype = $v;\n                        break;\n                    }\n                }\n            }\n\n            if ($tag == 'BODY') {\n                $this->SetDefaultFont($fonttype);\n            }\n            $this->SetFont($fonttype, $this->currentfontstyle, 0, false);\n        } else {\n            $this->SetFont($this->currentfontfamily, $this->currentfontstyle, 0, false);\n        }\n\n        foreach ($arrayaux as $k => $v) {\n            if ($type != 'INLINE' && $tag != 'BODY' && $type != 'TABLECELL') {\n                switch ($k) {\n                    // BORDERS\n                    case 'BORDER-TOP':\n                        $this->blk[$this->blklvl]['border_top'] = $this->border_details($v);\n                        if ($this->blk[$this->blklvl]['border_top']['s']) {\n                            $this->blk[$this->blklvl]['border'] = 1;\n                        }\n                        break;\n                    case 'BORDER-BOTTOM':\n                        $this->blk[$this->blklvl]['border_bottom'] = $this->border_details($v);\n                        if ($this->blk[$this->blklvl]['border_bottom']['s']) {\n                            $this->blk[$this->blklvl]['border'] = 1;\n                        }\n                        break;\n                    case 'BORDER-LEFT':\n                        $this->blk[$this->blklvl]['border_left'] = $this->border_details($v);\n                        if ($this->blk[$this->blklvl]['border_left']['s']) {\n                            $this->blk[$this->blklvl]['border'] = 1;\n                        }\n                        break;\n                    case 'BORDER-RIGHT':\n                        $this->blk[$this->blklvl]['border_right'] = $this->border_details($v);\n                        if ($this->blk[$this->blklvl]['border_right']['s']) {\n                            $this->blk[$this->blklvl]['border'] = 1;\n                        }\n                        break;\n\n                    // PADDING\n                    case 'PADDING-TOP':\n                        $this->blk[$this->blklvl]['padding_top'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'PADDING-BOTTOM':\n                        $this->blk[$this->blklvl]['padding_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'PADDING-LEFT':\n                        if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {\n                            $this->blk[$this->blklvl]['padding_left'] = 'auto';\n                            break;\n                        }\n                        $this->blk[$this->blklvl]['padding_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'PADDING-RIGHT':\n                        if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {\n                            $this->blk[$this->blklvl]['padding_right'] = 'auto';\n                            break;\n                        }\n                        $this->blk[$this->blklvl]['padding_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n\n                    // MARGINS\n                    case 'MARGIN-TOP':\n                        $tmp = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        if (isset($this->blk[$this->blklvl]['lastbottommargin'])) {\n                            if ($tmp > $this->blk[$this->blklvl]['lastbottommargin']) {\n                                $tmp -= $this->blk[$this->blklvl]['lastbottommargin'];\n                            } else {\n                                $tmp = 0;\n                            }\n                        }\n                        $this->blk[$this->blklvl]['margin_top'] = $tmp;\n                        break;\n                    case 'MARGIN-BOTTOM':\n                        $this->blk[$this->blklvl]['margin_bottom'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'MARGIN-LEFT':\n                        $this->blk[$this->blklvl]['margin_left'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'MARGIN-RIGHT':\n                        $this->blk[$this->blklvl]['margin_right'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n\n                    /* -- BORDER-RADIUS -- */\n                    case 'BORDER-TOP-LEFT-RADIUS-H':\n                        $this->blk[$this->blklvl]['border_radius_TL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'BORDER-TOP-LEFT-RADIUS-V':\n                        $this->blk[$this->blklvl]['border_radius_TL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'BORDER-TOP-RIGHT-RADIUS-H':\n                        $this->blk[$this->blklvl]['border_radius_TR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'BORDER-TOP-RIGHT-RADIUS-V':\n                        $this->blk[$this->blklvl]['border_radius_TR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'BORDER-BOTTOM-LEFT-RADIUS-H':\n                        $this->blk[$this->blklvl]['border_radius_BL_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'BORDER-BOTTOM-LEFT-RADIUS-V':\n                        $this->blk[$this->blklvl]['border_radius_BL_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'BORDER-BOTTOM-RIGHT-RADIUS-H':\n                        $this->blk[$this->blklvl]['border_radius_BR_H'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    case 'BORDER-BOTTOM-RIGHT-RADIUS-V':\n                        $this->blk[$this->blklvl]['border_radius_BR_V'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        break;\n                    /* -- END BORDER-RADIUS -- */\n\n                    case 'BOX-SHADOW':\n                        $bs = $this->cssManager->setCSSboxshadow($v);\n                        if ($bs) {\n                            $this->blk[$this->blklvl]['box_shadow'] = $bs;\n                        }\n                        break;\n\n                    case 'BACKGROUND-CLIP':\n                        if (strtoupper($v) == 'PADDING-BOX') {\n                            $this->blk[$this->blklvl]['background_clip'] = 'padding-box';\n                        } elseif (strtoupper($v) == 'CONTENT-BOX') {\n                            $this->blk[$this->blklvl]['background_clip'] = 'content-box';\n                        }\n                        break;\n\n                    case 'PAGE-BREAK-AFTER':\n                        if (strtoupper($v) == 'AVOID') {\n                            $this->blk[$this->blklvl]['page_break_after_avoid'] = true;\n                        } elseif (strtoupper($v) == 'ALWAYS' || strtoupper($v) == 'LEFT' || strtoupper($v) == 'RIGHT') {\n                            $this->blk[$this->blklvl]['page_break_after'] = strtoupper($v);\n                        }\n                        break;\n\n                    // mPDF 6 pagebreaktype\n                    case 'BOX-DECORATION-BREAK':\n                        if (strtoupper($v) == 'CLONE') {\n                            $this->blk[$this->blklvl]['box_decoration_break'] = 'clone';\n                        } elseif (strtoupper($v) == 'SLICE') {\n                            $this->blk[$this->blklvl]['box_decoration_break'] = 'slice';\n                        }\n                        break;\n\n                    case 'WIDTH':\n                        if (strtoupper($v) != 'AUTO') {\n                            $this->blk[$this->blklvl]['css_set_width'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);\n                        }\n                        break;\n\n                    // mPDF 6  Lists\n                    // LISTS\n                    case 'LIST-STYLE-TYPE':\n                        $this->blk[$this->blklvl]['list_style_type'] = strtolower($v);\n                        break;\n                    case 'LIST-STYLE-IMAGE':\n                        $this->blk[$this->blklvl]['list_style_image'] = strtolower($v);\n                        break;\n                    case 'LIST-STYLE-POSITION':\n                        $this->blk[$this->blklvl]['list_style_position'] = strtolower($v);\n                        break;\n                }//end of switch($k)\n            }\n\n\n            if ($type != 'INLINE' && $type != 'TABLECELL') { // All block-level, including BODY tag\n                switch ($k) {\n                    case 'TEXT-INDENT':\n                        // Computed value - to inherit\n                        $this->blk[$this->blklvl]['text_indent'] = $this->sizeConverter->convert($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false) . 'mm';\n                        break;\n\n                    case 'MARGIN-COLLAPSE': // Custom tag to collapse margins at top and bottom of page\n                        if (strtoupper($v) == 'COLLAPSE') {\n                            $this->blk[$this->blklvl]['margin_collapse'] = true;\n                        }\n                        break;\n\n                    case 'LINE-HEIGHT':\n                        $this->blk[$this->blklvl]['line_height'] = $this->fixLineheight($v);\n                        if (!$this->blk[$this->blklvl]['line_height']) {\n                            $this->blk[$this->blklvl]['line_height'] = 'N';\n                        } // mPDF 6\n                        break;\n\n                    // mPDF 6\n                    case 'LINE-STACKING-STRATEGY':\n                        $this->blk[$this->blklvl]['line_stacking_strategy'] = strtolower($v);\n                        break;\n\n                    case 'LINE-STACKING-SHIFT':\n                        $this->blk[$this->blklvl]['line_stacking_shift'] = strtolower($v);\n                        break;\n\n                    case 'TEXT-ALIGN': // left right center justify\n                        switch (strtoupper($v)) {\n                            case 'LEFT':\n                                $this->blk[$this->blklvl]['align'] = \"L\";\n                                break;\n                            case 'CENTER':\n                                $this->blk[$this->blklvl]['align'] = \"C\";\n                                break;\n                            case 'RIGHT':\n                                $this->blk[$this->blklvl]['align'] = \"R\";\n                                break;\n                            case 'JUSTIFY':\n                                $this->blk[$this->blklvl]['align'] = \"J\";\n                                break;\n                        }\n                        break;\n\n                    /* -- BACKGROUNDS -- */\n                    case 'BACKGROUND-GRADIENT':\n                        if ($type == 'BLOCK') {\n                            $this->blk[$this->blklvl]['gradient'] = $v;\n                        }\n                        break;\n                    /* -- END BACKGROUNDS -- */\n\n                    case 'DIRECTION':\n                        if ($v) {\n                            $this->blk[$this->blklvl]['direction'] = strtolower($v);\n                        }\n                        break;\n                }\n            }\n\n            // FOR INLINE ONLY\n            if ($type == 'INLINE') {\n                switch ($k) {\n                    case 'DISPLAY':\n                        if (strtoupper($v) == 'NONE') {\n                            $this->inlineDisplayOff = true;\n                        }\n                        break;\n                    case 'DIRECTION':\n                        break;\n                }\n            }\n            // FOR INLINE ONLY\n            if ($type == 'INLINE') {\n                switch ($k) {\n                    // BORDERS\n                    case 'BORDER-TOP':\n                        $this->spanborddet['T'] = $this->border_details($v);\n                        $this->spanborder = true;\n                        $spanbordset = true;\n                        break;\n                    case 'BORDER-BOTTOM':\n                        $this->spanborddet['B'] = $this->border_details($v);\n                        $this->spanborder = true;\n                        $spanbordset = true;\n                        break;\n                    case 'BORDER-LEFT':\n                        $this->spanborddet['L'] = $this->border_details($v);\n                        $this->spanborder = true;\n                        $spanbordset = true;\n                        break;\n                    case 'BORDER-RIGHT':\n                        $this->spanborddet['R'] = $this->border_details($v);\n                        $this->spanborder = true;\n                        $spanbordset = true;\n                        break;\n                    case 'VISIBILITY': // block is set in OpenTag\n                        $v = strtolower($v);\n                        if ($v == 'visible' || $v == 'hidden' || $v == 'printonly' || $v == 'screenonly') {\n                            $this->textparam['visibility'] = $v;\n                        }\n                        break;\n                }//end of switch($k)\n            }\n\n            if ($type != 'TABLECELL') {\n                // FOR INLINE and BLOCK\n                switch ($k) {\n                    case 'TEXT-ALIGN': // left right center justify\n                        if (strtoupper($v) == 'NOJUSTIFY' && $this->blk[$this->blklvl]['align'] == \"J\") {\n                            $this->blk[$this->blklvl]['align'] = \"\";\n                        }\n                        break;\n                    // bgcolor only - to stay consistent with original html2fpdf\n                    case 'BACKGROUND':\n                    case 'BACKGROUND-COLOR':\n                        $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);\n                        if ($cor) {\n                            if ($tag == 'BODY') {\n                                $this->bodyBackgroundColor = $cor;\n                            } elseif ($type == 'INLINE') {\n                                $this->spanbgcolorarray = $cor;\n                                $this->spanbgcolor = true;\n                                $spanbgset = true;\n                            } else {\n                                $this->blk[$this->blklvl]['bgcolorarray'] = $cor;\n                                $this->blk[$this->blklvl]['bgcolor'] = true;\n                            }\n                        } elseif ($type != 'INLINE') {\n                            if ($this->ColActive) {\n                                $this->blk[$this->blklvl]['bgcolorarray'] = $this->blk[$prevlevel]['bgcolorarray'];\n                                $this->blk[$this->blklvl]['bgcolor'] = $this->blk[$prevlevel]['bgcolor'];\n                            }\n                        }\n                        break;\n\n                    case 'VERTICAL-ALIGN': // super and sub only dealt with here e.g. <SUB> and <SUP>\n                        switch (strtoupper($v)) {\n                            case 'SUPER':\n                                $this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT); // mPDF 5.7.1\n                                $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);\n                                // mPDF 5.7.3  inline text-decoration parameters\n                                if (isset($this->textparam['text-baseline'])) {\n                                    $this->textparam['text-baseline'] += ($this->baselineSup) * $preceeding_fontsize;\n                                } else {\n                                    $this->textparam['text-baseline'] = ($this->baselineSup) * $preceeding_fontsize;\n                                }\n                                break;\n                            case 'SUB':\n                                $this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);\n                                $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);\n                                // mPDF 5.7.3  inline text-decoration parameters\n                                if (isset($this->textparam['text-baseline'])) {\n                                    $this->textparam['text-baseline'] += ($this->baselineSub) * $preceeding_fontsize;\n                                } else {\n                                    $this->textparam['text-baseline'] = ($this->baselineSub) * $preceeding_fontsize;\n                                }\n                                break;\n                            case 'BASELINE':\n                                $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);\n                                $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);\n                                // mPDF 5.7.3  inline text-decoration parameters\n                                if (isset($this->textparam['text-baseline'])) {\n                                    unset($this->textparam['text-baseline']);\n                                }\n                                break;\n                            // mPDF 5.7.3  inline text-decoration parameters\n                            default:\n                                $lh = $this->_computeLineheight($this->blk[$this->blklvl]['line_height']);\n                                $sz = $this->sizeConverter->convert($v, $lh, $this->FontSize, false);\n                                $this->textvar = ($this->textvar & ~TextVars::FA_SUBSCRIPT);\n                                $this->textvar = ($this->textvar & ~TextVars::FA_SUPERSCRIPT);\n                                if ($sz) {\n                                    if ($sz > 0) {\n                                        $this->textvar = ($this->textvar | TextVars::FA_SUPERSCRIPT);\n                                    } else {\n                                        $this->textvar = ($this->textvar | TextVars::FA_SUBSCRIPT);\n                                    }\n                                    if (isset($this->textparam['text-baseline'])) {\n                                        $this->textparam['text-baseline'] += $sz;\n                                    } else {\n                                        $this->textparam['text-baseline'] = $sz;\n                                    }\n                                }\n                        }\n                        break;\n                }//end of switch($k)\n            }\n\n\n            // FOR ALL\n            switch ($k) {\n                case 'LETTER-SPACING':\n                    $this->lSpacingCSS = $v;\n                    if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {\n                        $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize);\n                    }\n                    break;\n\n                case 'WORD-SPACING':\n                    $this->wSpacingCSS = $v;\n                    if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {\n                        $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize);\n                    }\n                    break;\n\n                case 'FONT-STYLE': // italic normal oblique\n                    switch (strtoupper($v)) {\n                        case 'ITALIC':\n                        case 'OBLIQUE':\n                            $this->SetStyle('I', true);\n                            break;\n                        case 'NORMAL':\n                            $this->SetStyle('I', false);\n                            break;\n                    }\n                    break;\n\n                case 'FONT-WEIGHT': // normal bold // Does not support: bolder, lighter, 100..900(step value=100)\n                    switch (strtoupper($v)) {\n                        case 'BOLD':\n                            $this->SetStyle('B', true);\n                            break;\n                        case 'NORMAL':\n                            $this->SetStyle('B', false);\n                            break;\n                    }\n                    break;\n\n                case 'FONT-KERNING':\n                    if (strtoupper($v) == 'NORMAL' || (strtoupper($v) == 'AUTO' && $this->useKerning)) {\n                        /* -- OTL -- */\n                        if ($this->CurrentFont['haskernGPOS']) {\n                            if (isset($this->OTLtags['Plus'])) {\n                                $this->OTLtags['Plus'] .= ' kern';\n                            } else {\n                                $this->OTLtags['Plus'] = ' kern';\n                            }\n                        } /* -- END OTL -- */ else {  // *OTL*\n                            $this->textvar = ($this->textvar | TextVars::FC_KERNING);\n                        } // *OTL*\n                    } elseif (strtoupper($v) == 'NONE' || (strtoupper($v) == 'AUTO' && !$this->useKerning)) {\n                        if (isset($this->OTLtags['Plus'])) {\n                            $this->OTLtags['Plus'] = str_replace('kern', '', $this->OTLtags['Plus']); // *OTL*\n                        }\n                        if (isset($this->OTLtags['FFPlus'])) {\n                            $this->OTLtags['FFPlus'] = preg_replace('/kern[\\d]*/', '', $this->OTLtags['FFPlus']);\n                        }\n                        $this->textvar = ($this->textvar & ~TextVars::FC_KERNING);\n                    }\n                    break;\n\n                /* -- OTL -- */\n                case 'FONT-LANGUAGE-OVERRIDE':\n                    $v = strtoupper($v);\n                    if (strpos($v, 'NORMAL') !== false) {\n                        $this->fontLanguageOverride = '';\n                    } else {\n                        $this->fontLanguageOverride = trim($v);\n                    }\n                    break;\n\n\n                case 'FONT-VARIANT-POSITION':\n                    if (isset($this->OTLtags['Plus'])) {\n                        $this->OTLtags['Plus'] = str_replace(['sups', 'subs'], '', $this->OTLtags['Plus']);\n                    }\n                    switch (strtoupper($v)) {\n                        case 'SUPER':\n                            $this->OTLtags['Plus'] .= ' sups';\n                            break;\n                        case 'SUB':\n                            $this->OTLtags['Plus'] .= ' subs';\n                            break;\n                        case 'NORMAL':\n                            break;\n                    }\n                    break;\n\n                case 'FONT-VARIANT-CAPS':\n                    $v = strtoupper($v);\n                    if (!isset($this->OTLtags['Plus'])) {\n                        $this->OTLtags['Plus'] = '';\n                    }\n                    $this->OTLtags['Plus'] = str_replace(['c2sc', 'smcp', 'c2pc', 'pcap', 'unic', 'titl'], '', $this->OTLtags['Plus']);\n                    $this->textvar = ($this->textvar & ~TextVars::FC_SMALLCAPS);   // ?????????????? <small-caps>\n                    if (strpos($v, 'ALL-SMALL-CAPS') !== false) {\n                        $this->OTLtags['Plus'] .= ' c2sc smcp';\n                    } elseif (strpos($v, 'SMALL-CAPS') !== false) {\n                        if (isset($this->CurrentFont['hassmallcapsGSUB']) && $this->CurrentFont['hassmallcapsGSUB']) {\n                            $this->OTLtags['Plus'] .= ' smcp';\n                        } else {\n                            $this->textvar = ($this->textvar | TextVars::FC_SMALLCAPS);\n                        }\n                    } elseif (strpos($v, 'ALL-PETITE-CAPS') !== false) {\n                        $this->OTLtags['Plus'] .= ' c2pc pcap';\n                    } elseif (strpos($v, 'PETITE-CAPS') !== false) {\n                        $this->OTLtags['Plus'] .= ' pcap';\n                    } elseif (strpos($v, 'UNICASE') !== false) {\n                        $this->OTLtags['Plus'] .= ' unic';\n                    } elseif (strpos($v, 'TITLING-CAPS') !== false) {\n                        $this->OTLtags['Plus'] .= ' titl';\n                    }\n                    break;\n\n                case 'FONT-VARIANT-LIGATURES':\n                    $v = strtoupper($v);\n                    if (!isset($this->OTLtags['Plus'])) {\n                        $this->OTLtags['Plus'] = '';\n                    }\n                    if (!isset($this->OTLtags['Minus'])) {\n                        $this->OTLtags['Minus'] = '';\n                    }\n                    if (strpos($v, 'NORMAL') !== false) {\n                        $this->OTLtags['Minus'] = str_replace(['liga', 'clig', 'calt'], '', $this->OTLtags['Minus']);\n                        $this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);\n                    } elseif (strpos($v, 'NONE') !== false) {\n                        $this->OTLtags['Minus'] .= ' liga clig calt';\n                        $this->OTLtags['Plus'] = str_replace(['dlig', 'hlig'], '', $this->OTLtags['Plus']);\n                    }\n                    if (strpos($v, 'NO-COMMON-LIGATURES') !== false) {\n                        $this->OTLtags['Minus'] .= ' liga clig';\n                    } elseif (strpos($v, 'COMMON-LIGATURES') !== false) {\n                        $this->OTLtags['Minus'] = str_replace(['liga', 'clig'], '', $this->OTLtags['Minus']);\n                    }\n                    if (strpos($v, 'NO-CONTEXTUAL') !== false) {\n                        $this->OTLtags['Minus'] .= ' calt';\n                    } elseif (strpos($v, 'CONTEXTUAL') !== false) {\n                        $this->OTLtags['Minus'] = str_replace('calt', '', $this->OTLtags['Minus']);\n                    }\n                    if (strpos($v, 'NO-DISCRETIONARY-LIGATURES') !== false) {\n                        $this->OTLtags['Plus'] = str_replace('dlig', '', $this->OTLtags['Plus']);\n                    } elseif (strpos($v, 'DISCRETIONARY-LIGATURES') !== false) {\n                        $this->OTLtags['Plus'] .= ' dlig';\n                    }\n                    if (strpos($v, 'NO-HISTORICAL-LIGATURES') !== false) {\n                        $this->OTLtags['Plus'] = str_replace('hlig', '', $this->OTLtags['Plus']);\n                    } elseif (strpos($v, 'HISTORICAL-LIGATURES') !== false) {\n                        $this->OTLtags['Plus'] .= ' hlig';\n                    }\n\n                    break;\n\n                case 'FONT-VARIANT-NUMERIC':\n                    $v = strtoupper($v);\n                    if (!isset($this->OTLtags['Plus'])) {\n                        $this->OTLtags['Plus'] = '';\n                    }\n                    if (strpos($v, 'NORMAL') !== false) {\n                        $this->OTLtags['Plus'] = str_replace(['ordn', 'zero', 'lnum', 'onum', 'pnum', 'tnum', 'frac', 'afrc'], '', $this->OTLtags['Plus']);\n                    }\n                    if (strpos($v, 'ORDINAL') !== false) {\n                        $this->OTLtags['Plus'] .= ' ordn';\n                    }\n                    if (strpos($v, 'SLASHED-ZERO') !== false) {\n                        $this->OTLtags['Plus'] .= ' zero';\n                    }\n                    if (strpos($v, 'LINING-NUMS') !== false) {\n                        $this->OTLtags['Plus'] .= ' lnum';\n                        $this->OTLtags['Plus'] = str_replace('onum', '', $this->OTLtags['Plus']);\n                    } elseif (strpos($v, 'OLDSTYLE-NUMS') !== false) {\n                        $this->OTLtags['Plus'] .= ' onum';\n                        $this->OTLtags['Plus'] = str_replace('lnum', '', $this->OTLtags['Plus']);\n                    }\n                    if (strpos($v, 'PROPORTIONAL-NUMS') !== false) {\n                        $this->OTLtags['Plus'] .= ' pnum';\n                        $this->OTLtags['Plus'] = str_replace('tnum', '', $this->OTLtags['Plus']);\n                    } elseif (strpos($v, 'TABULAR-NUMS') !== false) {\n                        $this->OTLtags['Plus'] .= ' tnum';\n                        $this->OTLtags['Plus'] = str_replace('pnum', '', $this->OTLtags['Plus']);\n                    }\n                    if (strpos($v, 'DIAGONAL-FRACTIONS') !== false) {\n                        $this->OTLtags['Plus'] .= ' frac';\n                        $this->OTLtags['Plus'] = str_replace('afrc', '', $this->OTLtags['Plus']);\n                    } elseif (strpos($v, 'STACKED-FRACTIONS') !== false) {\n                        $this->OTLtags['Plus'] .= ' afrc';\n                        $this->OTLtags['Plus'] = str_replace('frac', '', $this->OTLtags['Plus']);\n                    }\n                    break;\n\n                case 'FONT-VARIANT-ALTERNATES':  // Only supports historical-forms\n                    $v = strtoupper($v);\n                    if (!isset($this->OTLtags['Plus'])) {\n                        $this->OTLtags['Plus'] = '';\n                    }\n                    if (strpos($v, 'NORMAL') !== false) {\n                        $this->OTLtags['Plus'] = str_replace('hist', '', $this->OTLtags['Plus']);\n                    }\n                    if (strpos($v, 'HISTORICAL-FORMS') !== false) {\n                        $this->OTLtags['Plus'] .= ' hist';\n                    }\n                    break;\n\n\n                case 'FONT-FEATURE-SETTINGS':\n                    $v = strtolower($v);\n                    if (strpos($v, 'normal') !== false) {\n                        $this->OTLtags['FFMinus'] = '';\n                        $this->OTLtags['FFPlus'] = '';\n                    } else {\n                        if (!isset($this->OTLtags['FFPlus'])) {\n                            $this->OTLtags['FFPlus'] = '';\n                        }\n                        if (!isset($this->OTLtags['FFMinus'])) {\n                            $this->OTLtags['FFMinus'] = '';\n                        }\n                        $tags = preg_split('/[,]/', $v);\n                        foreach ($tags as $t) {\n                            if (preg_match('/[\\\"\\']([a-zA-Z0-9]{4})[\\\"\\']\\s*(on|off|\\d*){0,1}/', $t, $m)) {\n                                if ($m[2] == 'off' || $m[2] === '0') {\n                                    if (strpos($this->OTLtags['FFMinus'], $m[1]) === false) {\n                                        $this->OTLtags['FFMinus'] .= ' ' . $m[1];\n                                    }\n                                    $this->OTLtags['FFPlus'] = preg_replace('/' . $m[1] . '[\\d]*/', '', $this->OTLtags['FFPlus']);\n                                } else {\n                                    if ($m[2] == 'on') {\n                                        $m[2] = '1';\n                                    }\n                                    if (strpos($this->OTLtags['FFPlus'], $m[1]) === false) {\n                                        $this->OTLtags['FFPlus'] .= ' ' . $m[1] . $m[2];\n                                    }\n                                    $this->OTLtags['FFMinus'] = str_replace($m[1], '', $this->OTLtags['FFMinus']);\n                                }\n                            }\n                        }\n                    }\n                    break;\n                /* -- END OTL -- */\n\n\n                case 'TEXT-TRANSFORM': // none uppercase lowercase // Does support: capitalize\n                    switch (strtoupper($v)) { // Not working 100%\n                        case 'CAPITALIZE':\n                            $this->textvar = ($this->textvar | TextVars::FT_CAPITALIZE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1\n                            break;\n                        case 'UPPERCASE':\n                            $this->textvar = ($this->textvar | TextVars::FT_UPPERCASE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1\n                            break;\n                        case 'LOWERCASE':\n                            $this->textvar = ($this->textvar | TextVars::FT_LOWERCASE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1\n                            break;\n                        case 'NONE':\n                            break;\n                            $this->textvar = ($this->textvar & ~TextVars::FT_UPPERCASE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_LOWERCASE); // mPDF 5.7.1\n                            $this->textvar = ($this->textvar & ~TextVars::FT_CAPITALIZE); // mPDF 5.7.1\n                    }\n                    break;\n\n                case 'TEXT-SHADOW':\n                    $ts = $this->cssManager->setCSStextshadow($v);\n                    if ($ts) {\n                        $this->textshadow = $ts;\n                    }\n                    break;\n\n                case 'HYPHENS':\n                    if (strtoupper($v) == 'NONE') {\n                        $this->textparam['hyphens'] = 2;\n                    } elseif (strtoupper($v) == 'AUTO') {\n                        $this->textparam['hyphens'] = 1;\n                    } elseif (strtoupper($v) == 'MANUAL') {\n                        $this->textparam['hyphens'] = 0;\n                    }\n                    break;\n\n                case 'TEXT-OUTLINE':\n                    if (strtoupper($v) == 'NONE') {\n                        $this->textparam['outline-s'] = false;\n                    }\n                    break;\n\n                case 'TEXT-OUTLINE-WIDTH':\n                case 'OUTLINE-WIDTH':\n                    switch (strtoupper($v)) {\n                        case 'THIN':\n                            $v = '0.03em';\n                            break;\n                        case 'MEDIUM':\n                            $v = '0.05em';\n                            break;\n                        case 'THICK':\n                            $v = '0.07em';\n                            break;\n                    }\n                    $w = $this->sizeConverter->convert($v, $this->FontSize, $this->FontSize);\n                    if ($w) {\n                        $this->textparam['outline-WIDTH'] = $w;\n                        $this->textparam['outline-s'] = true;\n                    } else {\n                        $this->textparam['outline-s'] = false;\n                    }\n                    break;\n\n                case 'TEXT-OUTLINE-COLOR':\n                case 'OUTLINE-COLOR':\n                    if (strtoupper($v) == 'INVERT') {\n                        if ($this->colorarray) {\n                            $cor = $this->colorarray;\n                            $this->textparam['outline-COLOR'] = $this->colorConverter->invert($cor);\n                        } else {\n                            $this->textparam['outline-COLOR'] = $this->colorConverter->convert(255, $this->PDFAXwarnings);\n                        }\n                    } else {\n                        $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);\n                        if ($cor) {\n                            $this->textparam['outline-COLOR'] = $cor;\n                        }\n                    }\n                    break;\n\n                case 'COLOR': // font color\n                    $cor = $this->colorConverter->convert($v, $this->PDFAXwarnings);\n                    if ($cor) {\n                        $this->colorarray = $cor;\n                        $this->SetTColor($cor);\n                    }\n                    break;\n            }//end of switch($k)\n        }//end of foreach\n        // mPDF 5.7.3  inline text-decoration parameters\n        // Needs to be set at the end - after vertical-align = super/sub, so that textparam['text-baseline'] is set\n        if (isset($arrayaux['TEXT-DECORATION'])) {\n            $v = $arrayaux['TEXT-DECORATION']; // none underline line-through (strikeout) // Does not support: blink\n            if (stristr($v, 'LINE-THROUGH')) {\n                $this->textvar = ($this->textvar | TextVars::FD_LINETHROUGH);\n                // mPDF 5.7.3  inline text-decoration parameters\n                if (isset($this->textparam['text-baseline'])) {\n                    $this->textparam['s-decoration']['baseline'] = $this->textparam['text-baseline'];\n                } else {\n                    $this->textparam['s-decoration']['baseline'] = 0;\n                }\n                $this->textparam['s-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;\n                $this->textparam['s-decoration']['fontsize'] = $this->FontSize;\n                $this->textparam['s-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG\n            }\n            if (stristr($v, 'UNDERLINE')) {\n                $this->textvar = ($this->textvar | TextVars::FD_UNDERLINE);\n                // mPDF 5.7.3  inline text-decoration parameters\n                if (isset($this->textparam['text-baseline'])) {\n                    $this->textparam['u-decoration']['baseline'] = $this->textparam['text-baseline'];\n                } else {\n                    $this->textparam['u-decoration']['baseline'] = 0;\n                }\n                $this->textparam['u-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;\n                $this->textparam['u-decoration']['fontsize'] = $this->FontSize;\n                $this->textparam['u-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG\n            }\n            if (stristr($v, 'OVERLINE')) {\n                $this->textvar = ($this->textvar | TextVars::FD_OVERLINE);\n                // mPDF 5.7.3  inline text-decoration parameters\n                if (isset($this->textparam['text-baseline'])) {\n                    $this->textparam['o-decoration']['baseline'] = $this->textparam['text-baseline'];\n                } else {\n                    $this->textparam['o-decoration']['baseline'] = 0;\n                }\n                $this->textparam['o-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;\n                $this->textparam['o-decoration']['fontsize'] = $this->FontSize;\n                $this->textparam['o-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG\n            }\n            if (stristr($v, 'NONE')) {\n                $this->textvar = ($this->textvar & ~TextVars::FD_UNDERLINE);\n                $this->textvar = ($this->textvar & ~TextVars::FD_LINETHROUGH);\n                $this->textvar = ($this->textvar & ~TextVars::FD_OVERLINE);\n                // mPDF 5.7.3  inline text-decoration parameters\n                if (isset($this->textparam['u-decoration'])) {\n                    unset($this->textparam['u-decoration']);\n                }\n                if (isset($this->textparam['s-decoration'])) {\n                    unset($this->textparam['s-decoration']);\n                }\n                if (isset($this->textparam['o-decoration'])) {\n                    unset($this->textparam['o-decoration']);\n                }\n            }\n        }\n        // mPDF 6\n        if ($spanbordset) { // BORDER has been set on this INLINE element\n            if (isset($this->textparam['text-baseline'])) {\n                $this->textparam['bord-decoration']['baseline'] = $this->textparam['text-baseline'];\n            } else {\n                $this->textparam['bord-decoration']['baseline'] = 0;\n            }\n            $this->textparam['bord-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;\n            $this->textparam['bord-decoration']['fontsize'] = $this->FontSize;\n        }\n        if ($spanbgset) { // BACKGROUND[-COLOR] has been set on this INLINE element\n            if (isset($this->textparam['text-baseline'])) {\n                $this->textparam['bg-decoration']['baseline'] = $this->textparam['text-baseline'];\n            } else {\n                $this->textparam['bg-decoration']['baseline'] = 0;\n            }\n            $this->textparam['bg-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;\n            $this->textparam['bg-decoration']['fontsize'] = $this->FontSize;\n        }\n    }\n\n    /* -- END HTML-CSS -- */\n\n    function SetStyle($tag, $enable)\n    {\n        $this->$tag = $enable;\n        $style = '';\n        foreach (['B', 'I'] as $s) {\n            if ($this->$s) {\n                $style .= $s;\n            }\n        }\n        $this->currentfontstyle = $style;\n        $this->SetFont('', $style, 0, false);\n    }\n\n    // Set multiple styles at one time\n    function SetStylesArray($arr)\n    {\n        $style = '';\n        foreach (['B', 'I'] as $s) {\n            if (isset($arr[$s])) {\n                if ($arr[$s]) {\n                    $this->$s = true;\n                    $style .= $s;\n                } else {\n                    $this->$s = false;\n                }\n            } elseif ($this->$s) {\n                $style .= $s;\n            }\n        }\n        $this->currentfontstyle = $style;\n        $this->SetFont('', $style, 0, false);\n    }\n\n    // Set multiple styles at one $str e.g. \"BI\"\n    function SetStyles($str)\n    {\n        $style = '';\n        foreach (['B', 'I'] as $s) {\n            if (strpos($str, $s) !== false) {\n                $this->$s = true;\n                $style .= $s;\n            } else {\n                $this->$s = false;\n            }\n        }\n        $this->currentfontstyle = $style;\n        $this->SetFont('', $style, 0, false);\n    }\n\n    function ResetStyles()\n    {\n        foreach (['B', 'I'] as $s) {\n            $this->$s = false;\n        }\n        $this->currentfontstyle = '';\n        $this->SetFont('', '', 0, false);\n    }\n\n    function DisableTags($str = '')\n    {\n        if ($str == '') { // enable all tags\n            // Insert new supported tags in the long string below.\n            $this->enabledtags = \"<a><acronym><address><article><aside><b><bdi><bdo><big><blockquote><br><caption><center><cite><code><del><details><dd><div><dl><dt><em><fieldset><figcaption><figure><font><form><h1><h2><h3><h4><h5><h6><hgroup><hr><i><img><input><ins><kbd><legend><li><main><mark><meter><nav><ol><option><p><pre><progress><q><s><samp><section><select><small><span><strike><strong><sub><summary><sup><table><tbody><td><template><textarea><tfoot><th><thead><time><tr><tt><u><ul><var><footer><header><annotation><bookmark><textcircle><barcode><dottab><indexentry><indexinsert><watermarktext><watermarkimage><tts><ttz><tta><column_break><columnbreak><newcolumn><newpage><page_break><pagebreak><formfeed><columns><toc><tocentry><tocpagebreak><pageheader><pagefooter><setpageheader><setpagefooter><sethtmlpageheader><sethtmlpagefooter>\";\n        } else {\n            $str = explode(\",\", $str);\n            foreach ($str as $v) {\n                $this->enabledtags = str_replace(trim($v), '', $this->enabledtags);\n            }\n        }\n    }\n\n    /* -- TABLES -- */\n\n    function TableCheckMinWidth($maxwidth, $forcewrap = 0, $textbuffer = [], $checkletter = false)\n    {\n        // mPDF 6\n        $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)\n        $acclongest = 0; // mPDF 6 (accumulated length max across > 1 chunk)\n        $biggestword = 0;\n        $toonarrow = false;\n        if ((count($textbuffer) == 0) or ( (count($textbuffer) == 1) && ($textbuffer[0][0] == ''))) {\n            return 0;\n        }\n\n        foreach ($textbuffer as $chunk) {\n            $line = $chunk[0];\n            $OTLdata = (isset($chunk[18]) ? $chunk[18] : null);\n\n            // mPDF ITERATION\n            if ($this->iterationCounter) {\n                $line = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\\\1', $line);\n            }\n\n            // IMAGES & FORM ELEMENTS\n            if (substr($line, 0, 3) == \"\\xbb\\xa4\\xac\") { // inline object - FORM element or IMAGE!\n                $objattr = $this->_getObjAttr($line);\n                if ($objattr['type'] != 'hr' && isset($objattr['width']) && ($objattr['width'] / $this->shrin_k) > ($maxwidth + 0.0001)) {\n                    if (($objattr['width'] / $this->shrin_k) > $biggestword) {\n                        $biggestword = ($objattr['width'] / $this->shrin_k);\n                    }\n                    $toonarrow = true;\n                }\n                continue;\n            }\n\n            if ($line == \"\\n\") {\n                $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)\n                continue;\n            }\n            $line = trim($line);\n            if (!empty($OTLdata)) {\n                $this->otl->trimOTLdata($OTLdata, true, true);\n            } // *OTL*\n            // SET FONT SIZE/STYLE from $chunk[n]\n            // FONTSIZE\n            if (isset($chunk[11]) and $chunk[11] != '') {\n                if ($this->shrin_k) {\n                    $this->SetFontSize($chunk[11] / $this->shrin_k, false);\n                } else {\n                    $this->SetFontSize($chunk[11], false);\n                }\n            }\n            // FONTFAMILY\n            if (isset($chunk[4]) and $chunk[4] != '') {\n                $font = $this->SetFont($chunk[4], $this->FontStyle, 0, false);\n            }\n            // B I\n            if (isset($chunk[2]) and $chunk[2] != '') {\n                $this->SetStyles($chunk[2]);\n            }\n\n            $lbw = $rbw = 0; // Border widths\n            if (isset($chunk[16]) && !empty($chunk[16])) { // Border\n                $this->spanborddet = $chunk[16];\n                $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);\n                $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);\n            }\n            if (isset($chunk[15])) {   // Word spacing\n                $this->wSpacingCSS = $chunk[15];\n                if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {\n                    $this->minwSpacing = $this->sizeConverter->convert($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3\n                }\n            }\n            if (isset($chunk[14])) {   // Letter spacing\n                $this->lSpacingCSS = $chunk[14];\n                if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {\n                    $this->fixedlSpacing = $this->sizeConverter->convert($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3\n                }\n            }\n            if (isset($chunk[8])) { // mPDF 5.7.1\n                $this->textvar = $chunk[8];\n            }\n\n            // mPDF 6\n            // If overflow==wrap ($checkletter) OR (No word breaks and contains CJK)\n            if ($checkletter || (!preg_match('/(\\xe2\\x80\\x8b| )/', trim($line)) && preg_match(\"/([\" . $this->pregCJKchars . \"])/u\", $line) )) {\n                if (preg_match(\"/([\" . $this->pregCJKchars . \"])/u\", $line)) {\n                    $checkCJK = true;\n                } else {\n                    $checkCJK = false;\n                }\n\n                $letters = preg_split('//u', $line);\n                foreach ($letters as $k => $letter) {\n                    // mPDF 6\n                    if ($checkCJK) {\n                        if (preg_match(\"/[\" . $this->CJKleading . \"]/u\", $letter) && $k > 0) {\n                            $letter = $letters[$k - 1] . $letter;\n                        }\n                        if (preg_match(\"/[\" . $this->CJKfollowing . \"]/u\", $letter) && $k < (count($letters) - 1)) {\n                            $letter = $letter . $letters[$k + 1];\n                        }\n                    }\n\n                    $letterwidth = $this->GetStringWidth($letter, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here\n                    // so don't have to split OTLdata for each word\n                    if ($k == 0) {\n                        $letterwidth += $lbw;\n                    }\n                    if ($k == (count($letters) - 1)) {\n                        $letterwidth += $rbw;\n                    }\n\n                    // Warn user that maxwidth is insufficient\n                    if ($letterwidth > $maxwidth + 0.0001) {\n                        if ($letterwidth > $biggestword) {\n                            $biggestword = $letterwidth;\n                        }\n                        $toonarrow = true;\n                    }\n                }\n            } else {\n                // mPDF 6\n                // Need to account for any XAdvance in GPOSinfo (OTLdata = $chunk[18])\n                $wordXAdvance = [];\n                if (isset($chunk[18]) && $chunk[18]) {\n                    preg_match_all('/(\\xe2\\x80\\x8b| )/', $line, $spaces, PREG_OFFSET_CAPTURE); // U+200B Zero Width word boundary, or space\n                    $lastoffset = 0;\n                    $k = -1; // Added so that if no spaces found, \"last word\" later is calculated for the one and only word\n                    foreach ($spaces[0] as $k => $m) {\n                        $offset = $m[1];\n                        // ...TableCheckMinWidth...\n                        // At this point, BIDI not applied, Writing direction is not set, and XAdvanceL balances XAdvanceR\n                        for ($n = $lastoffset; $n < $offset; $n++) {\n                            if (isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {\n                                if (isset($wordXAdvance[$k])) {\n                                    $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];\n                                } else {\n                                    $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];\n                                }\n                            }\n                        }\n                        $lastoffset = $offset + 1;\n                    }\n\n                    $k++;  // last word\n                    foreach ($chunk[18]['GPOSinfo'] as $n => $gpos) {\n                        if ($n >= $lastoffset && isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {\n                            if (isset($wordXAdvance[$k])) {\n                                $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];\n                            } else {\n                                $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];\n                            }\n                        }\n                    }\n                }\n\n                $words = preg_split('/(\\xe2\\x80\\x8b| )/', $line); // U+200B Zero Width word boundary, or space\n                foreach ($words as $k => $word) {\n                    $word = trim($word);\n                    $wordwidth = $this->GetStringWidth($word, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here\n                    // so don't have to split OTLdata for each word\n                    if (isset($wordXAdvance[$k])) {\n                        $wordwidth += ($wordXAdvance[$k] * 1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000);\n                    }\n                    if ($k == 0) {\n                        $wordwidth += $lbw;\n                    }\n                    if ($k == (count($words) - 1)) {\n                        $wordwidth += $rbw;\n                    }\n\n                    // mPDF 6\n                    if (count($words) == 1 && substr($chunk[0], 0, 1) != ' ') {\n                        $acclength += $wordwidth;\n                    } elseif (count($words) > 1 && $k == 0 && substr($chunk[0], 0, 1) != ' ') {\n                        $acclength += $wordwidth;\n                    } else {\n                        $acclength = $wordwidth;\n                    }\n                    $acclongest = max($acclongest, $acclength);\n                    if (count($words) == 1 && substr($chunk[0], -1, 1) == ' ') {\n                        $acclength = 0;\n                    } elseif (count($words) > 1 && ($k != (count($words) - 1) || substr($chunk[0], -1, 1) == ' ')) {\n                        $acclength = 0;\n                    }\n\n                    // Warn user that maxwidth is insufficient\n                    if ($wordwidth > $maxwidth + 0.0001) {\n                        if ($wordwidth > $biggestword) {\n                            $biggestword = $wordwidth;\n                        }\n                        $toonarrow = true;\n                    }\n                }\n            }\n\n            // mPDF 6  Accumulated length of biggest word - across multiple chunks\n            if ($acclongest > $maxwidth + 0.0001) {\n                if ($acclongest > $biggestword) {\n                    $biggestword = $acclongest;\n                }\n                $toonarrow = true;\n            }\n\n            // RESET FONT SIZE/STYLE\n            // RESETTING VALUES\n            // Now we must deactivate what we have used\n            if (isset($chunk[2]) and $chunk[2] != '') {\n                $this->ResetStyles();\n            }\n            if (isset($chunk[4]) and $chunk[4] != '') {\n                $this->SetFont($this->default_font, $this->FontStyle, 0, false);\n            }\n            if (isset($chunk[11]) and $chunk[11] != '') {\n                $this->SetFontSize($this->default_font_size, false);\n            }\n            $this->spanborddet = [];\n            $this->textvar = 0x00; // mPDF 5.7.1\n            $this->OTLtags = [];\n            $this->lSpacingCSS = '';\n            $this->wSpacingCSS = '';\n            $this->fixedlSpacing = false;\n            $this->minwSpacing = 0;\n        }\n\n        // Return -(wordsize) if word is bigger than maxwidth\n        // ADDED\n        if (($toonarrow) && ($this->table_error_report)) {\n            throw new \\Mpdf\\MpdfException(\"Word is too long to fit in table - \" . $this->table_error_report_param);\n        }\n        if ($toonarrow) {\n            return -$biggestword;\n        } else {\n            return 1;\n        }\n    }\n\n    function shrinkTable(&$table, $k)\n    {\n        $table['border_spacing_H'] /= $k;\n        $table['border_spacing_V'] /= $k;\n\n        $table['padding']['T'] /= $k;\n        $table['padding']['R'] /= $k;\n        $table['padding']['B'] /= $k;\n        $table['padding']['L'] /= $k;\n\n        $table['margin']['T'] /= $k;\n        $table['margin']['R'] /= $k;\n        $table['margin']['B'] /= $k;\n        $table['margin']['L'] /= $k;\n\n        $table['border_details']['T']['w'] /= $k;\n        $table['border_details']['R']['w'] /= $k;\n        $table['border_details']['B']['w'] /= $k;\n        $table['border_details']['L']['w'] /= $k;\n\n        if (isset($table['max_cell_border_width']['T'])) {\n            $table['max_cell_border_width']['T'] /= $k;\n        }\n        if (isset($table['max_cell_border_width']['R'])) {\n            $table['max_cell_border_width']['R'] /= $k;\n        }\n        if (isset($table['max_cell_border_width']['B'])) {\n            $table['max_cell_border_width']['B'] /= $k;\n        }\n        if (isset($table['max_cell_border_width']['L'])) {\n            $table['max_cell_border_width']['L'] /= $k;\n        }\n\n        if ($this->simpleTables) {\n            $table['simple']['border_details']['T']['w'] /= $k;\n            $table['simple']['border_details']['R']['w'] /= $k;\n            $table['simple']['border_details']['B']['w'] /= $k;\n            $table['simple']['border_details']['L']['w'] /= $k;\n        }\n\n        $table['miw'] /= $k;\n        $table['maw'] /= $k;\n\n        for ($j = 0; $j < $table['nc']; $j++) { // columns\n\n            $table['wc'][$j]['miw'] = isset($table['wc'][$j]['miw']) ? $table['wc'][$j]['miw'] : 0;\n            $table['wc'][$j]['maw'] = isset($table['wc'][$j]['maw']) ? $table['wc'][$j]['maw'] : 0;\n\n            $table['wc'][$j]['miw'] /= $k;\n            $table['wc'][$j]['maw'] /= $k;\n\n            if (isset($table['decimal_align'][$j]['maxs0']) && $table['decimal_align'][$j]['maxs0']) {\n                $table['decimal_align'][$j]['maxs0'] /= $k;\n            }\n\n            if (isset($table['decimal_align'][$j]['maxs1']) && $table['decimal_align'][$j]['maxs1']) {\n                $table['decimal_align'][$j]['maxs1'] /= $k;\n            }\n\n            if (isset($table['wc'][$j]['absmiw']) && $table['wc'][$j]['absmiw']) {\n                $table['wc'][$j]['absmiw'] /= $k;\n            }\n\n            for ($i = 0; $i < $table['nr']; $i++) { // rows\n\n                $c = &$table['cells'][$i][$j];\n\n                if (isset($c) && $c) {\n\n                    if (!$this->simpleTables) {\n\n                        if ($this->packTableData) {\n\n                            $cell = $this->_unpackCellBorder($c['borderbin']);\n\n                            $cell['border_details']['T']['w'] /= $k;\n                            $cell['border_details']['R']['w'] /= $k;\n                            $cell['border_details']['B']['w'] /= $k;\n                            $cell['border_details']['L']['w'] /= $k;\n                            $cell['border_details']['mbw']['TL'] /= $k;\n                            $cell['border_details']['mbw']['TR'] /= $k;\n                            $cell['border_details']['mbw']['BL'] /= $k;\n                            $cell['border_details']['mbw']['BR'] /= $k;\n                            $cell['border_details']['mbw']['LT'] /= $k;\n                            $cell['border_details']['mbw']['LB'] /= $k;\n                            $cell['border_details']['mbw']['RT'] /= $k;\n                            $cell['border_details']['mbw']['RB'] /= $k;\n\n                            $c['borderbin'] = $this->_packCellBorder($cell);\n\n                        } else {\n\n                            $c['border_details']['T']['w'] /= $k;\n                            $c['border_details']['R']['w'] /= $k;\n                            $c['border_details']['B']['w'] /= $k;\n                            $c['border_details']['L']['w'] /= $k;\n                            $c['border_details']['mbw']['TL'] /= $k;\n                            $c['border_details']['mbw']['TR'] /= $k;\n                            $c['border_details']['mbw']['BL'] /= $k;\n                            $c['border_details']['mbw']['BR'] /= $k;\n                            $c['border_details']['mbw']['LT'] /= $k;\n                            $c['border_details']['mbw']['LB'] /= $k;\n                            $c['border_details']['mbw']['RT'] /= $k;\n                            $c['border_details']['mbw']['RB'] /= $k;\n                        }\n                    }\n\n                    $c['padding']['T'] /= $k;\n                    $c['padding']['R'] /= $k;\n                    $c['padding']['B'] /= $k;\n                    $c['padding']['L'] /= $k;\n\n                    $c['maxs'] = isset($c['maxs']) ? $c['maxs'] /= $k : null;\n                    $c['w'] = isset($c['w']) ? $c['w'] /= $k : null;\n\n                    $c['s'] = isset($c['s']) ? $c['s'] /= $k : 0;\n                    $c['h'] = isset($c['h']) ? $c['h'] /= $k : null;\n\n                    $c['miw'] = isset($c['miw']) ? $c['miw'] /= $k : 0;\n                    $c['maw'] = isset($c['maw']) ? $c['maw'] /= $k : 0;\n\n                    $c['absmiw'] = isset($c['absmiw']) ? $c['absmiw'] /= $k : null;\n\n                    $c['nestedmaw'] = isset($c['nestedmaw']) ? $c['nestedmaw'] /= $k : null;\n                    $c['nestedmiw'] = isset($c['nestedmiw']) ? $c['nestedmiw'] /= $k : null;\n\n                    if (isset($c['textbuffer'])) {\n                        foreach ($c['textbuffer'] as $n => $tb) {\n                            if (!empty($tb[16])) {\n                                !isset($c['textbuffer'][$n][16]['T']) || $c['textbuffer'][$n][16]['T']['w'] /= $k;\n                                !isset($c['textbuffer'][$n][16]['B']) || $c['textbuffer'][$n][16]['B']['w'] /= $k;\n                                !isset($c['textbuffer'][$n][16]['L']) || $c['textbuffer'][$n][16]['L']['w'] /= $k;\n                                !isset($c['textbuffer'][$n][16]['R']) || $c['textbuffer'][$n][16]['R']['w'] /= $k;\n                            }\n                        }\n                    }\n\n                    unset($c);\n                }\n\n            } // rows\n        } // columns\n    }\n\n    function read_short(&$fh)\n    {\n        $s = fread($fh, 2);\n        $a = (ord($s[0]) << 8) + ord($s[1]);\n        if ($a & (1 << 15)) {\n            $a = ($a - (1 << 16));\n        }\n        return $a;\n    }\n\n    function _packCellBorder($cell)\n    {\n        if (!is_array($cell) || !isset($cell)) {\n            return '';\n        }\n\n        if (!$this->packTableData) {\n            return $cell;\n        }\n        // = 186 bytes\n        $bindata = pack(\"nnda6A10nnda6A10nnda6A10nnda6A10nd9\", $cell['border'], $cell['border_details']['R']['s'], $cell['border_details']['R']['w'], $cell['border_details']['R']['c'], $cell['border_details']['R']['style'], $cell['border_details']['R']['dom'], $cell['border_details']['L']['s'], $cell['border_details']['L']['w'], $cell['border_details']['L']['c'], $cell['border_details']['L']['style'], $cell['border_details']['L']['dom'], $cell['border_details']['T']['s'], $cell['border_details']['T']['w'], $cell['border_details']['T']['c'], $cell['border_details']['T']['style'], $cell['border_details']['T']['dom'], $cell['border_details']['B']['s'], $cell['border_details']['B']['w'], $cell['border_details']['B']['c'], $cell['border_details']['B']['style'], $cell['border_details']['B']['dom'], $cell['border_details']['mbw']['BL'], $cell['border_details']['mbw']['BR'], $cell['border_details']['mbw']['RT'], $cell['border_details']['mbw']['RB'], $cell['border_details']['mbw']['TL'], $cell['border_details']['mbw']['TR'], $cell['border_details']['mbw']['LT'], $cell['border_details']['mbw']['LB'], (isset($cell['border_details']['cellposdom']) ? $cell['border_details']['cellposdom'] : 0));\n        return $bindata;\n    }\n\n    function _getBorderWidths($bindata)\n    {\n        if (!$bindata) {\n            return [0, 0, 0, 0];\n        }\n        if (!$this->packTableData) {\n            return [$bindata['border_details']['T']['w'], $bindata['border_details']['R']['w'], $bindata['border_details']['B']['w'], $bindata['border_details']['L']['w']];\n        }\n\n        $bd = unpack(\"nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd\", $bindata);\n        $cell['border_details']['R']['w'] = $bd['rw'];\n        $cell['border_details']['L']['w'] = $bd['lw'];\n        $cell['border_details']['T']['w'] = $bd['tw'];\n        $cell['border_details']['B']['w'] = $bd['bw'];\n        return [$bd['tw'], $bd['rw'], $bd['bw'], $bd['lw']];\n    }\n\n    function _unpackCellBorder($bindata)\n    {\n        if (!$bindata) {\n            return [];\n        }\n        if (!$this->packTableData) {\n            return $bindata;\n        }\n\n        $bd = unpack(\"nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd\", $bindata);\n\n        $cell['border'] = $bd['bord'];\n        $cell['border_details']['R']['s'] = $bd['rs'];\n        $cell['border_details']['R']['w'] = $bd['rw'];\n        $cell['border_details']['R']['c'] = str_pad($bd['rca'], 6, \"\\x00\");\n        $cell['border_details']['R']['style'] = trim($bd['rst']);\n        $cell['border_details']['R']['dom'] = $bd['rd'];\n\n        $cell['border_details']['L']['s'] = $bd['ls'];\n        $cell['border_details']['L']['w'] = $bd['lw'];\n        $cell['border_details']['L']['c'] = str_pad($bd['lca'], 6, \"\\x00\");\n        $cell['border_details']['L']['style'] = trim($bd['lst']);\n        $cell['border_details']['L']['dom'] = $bd['ld'];\n\n        $cell['border_details']['T']['s'] = $bd['ts'];\n        $cell['border_details']['T']['w'] = $bd['tw'];\n        $cell['border_details']['T']['c'] = str_pad($bd['tca'], 6, \"\\x00\");\n        $cell['border_details']['T']['style'] = trim($bd['tst']);\n        $cell['border_details']['T']['dom'] = $bd['td'];\n\n        $cell['border_details']['B']['s'] = $bd['bs'];\n        $cell['border_details']['B']['w'] = $bd['bw'];\n        $cell['border_details']['B']['c'] = str_pad($bd['bca'], 6, \"\\x00\");\n        $cell['border_details']['B']['style'] = trim($bd['bst']);\n        $cell['border_details']['B']['dom'] = $bd['bd'];\n\n        $cell['border_details']['mbw']['BL'] = $bd['mbl'];\n        $cell['border_details']['mbw']['BR'] = $bd['mbr'];\n        $cell['border_details']['mbw']['RT'] = $bd['mrt'];\n        $cell['border_details']['mbw']['RB'] = $bd['mrb'];\n        $cell['border_details']['mbw']['TL'] = $bd['mtl'];\n        $cell['border_details']['mbw']['TR'] = $bd['mtr'];\n        $cell['border_details']['mbw']['LT'] = $bd['mlt'];\n        $cell['border_details']['mbw']['LB'] = $bd['mlb'];\n        $cell['border_details']['cellposdom'] = $bd['cpd'];\n\n\n        return($cell);\n    }\n\n    ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////\n    ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////\n    ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////\n    // table\t\tArray of (w, h, bc, nr, wc, hr, cells)\n    // w\t\t\tWidth of table\n    // h\t\t\tHeight of table\n    // nc\t\t\tNumber column\n    // nr\t\t\tNumber row\n    // hr\t\t\tList of height of each row\n    // wc\t\t\tList of width of each column\n    // cells\t\tList of cells of each rows, cells[i][j] is a cell in the table\n    function _tableColumnWidth(&$table, $firstpass = false)\n    {\n        $cs = &$table['cells'];\n\n        $nc = $table['nc'];\n        $nr = $table['nr'];\n        $listspan = [];\n\n        if ($table['borders_separate']) {\n            $tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H'];\n        } else {\n            $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];\n        }\n\n        // ADDED table['l'][colno]\n        // = total length of text approx (using $c['s']) in that column - used to approximately distribute col widths in _tableWidth\n        //\n        for ($j = 0; $j < $nc; $j++) { // columns\n            $wc = &$table['wc'][$j];\n            for ($i = 0; $i < $nr; $i++) { // rows\n                if (isset($cs[$i][$j]) && $cs[$i][$j]) {\n                    $c = &$cs[$i][$j];\n\n                    if ($this->simpleTables) {\n                        if ($table['borders_separate']) { // NB twice border width\n                            $extrcw = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];\n                        } else {\n                            $extrcw = $table['simple']['border_details']['L']['w'] / 2 + $table['simple']['border_details']['R']['w'] / 2 + $c['padding']['L'] + $c['padding']['R'];\n                        }\n                    } else {\n                        if ($this->packTableData) {\n                            [$bt, $br, $bb, $bl] = $this->_getBorderWidths($c['borderbin']);\n                        } else {\n                            $br = $c['border_details']['R']['w'];\n                            $bl = $c['border_details']['L']['w'];\n                        }\n                        if ($table['borders_separate']) { // NB twice border width\n                            $extrcw = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];\n                        } else {\n                            $extrcw = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];\n                        }\n                    }\n\n                    // $mw = $this->GetStringWidth('W') + $extrcw ;\n                    $mw = $extrcw; // mPDF 6\n                    if (substr($c['a'], 0, 1) == 'D') {\n                        $mw = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'] + $extrcw;\n                    }\n\n                    $c['absmiw'] = $mw;\n\n                    if (isset($c['R']) && $c['R']) {\n                        $c['maw'] = $c['miw'] = $this->FontSize + $extrcw;\n                        if (isset($c['w'])) { // If cell width is specified\n                            if ($c['miw'] < $c['w']) {\n                                $c['miw'] = $c['w'];\n                            }\n                        }\n                        if (!isset($c['colspan'])) {\n                            if ($wc['miw'] < $c['miw']) {\n                                $wc['miw'] = $c['miw'];\n                            }\n                            if ($wc['maw'] < $c['maw']) {\n                                $wc['maw'] = $c['maw'];\n                            }\n\n                            if ($firstpass) {\n                                if (isset($table['l'][$j])) {\n                                    $table['l'][$j] += $c['miw'];\n                                } else {\n                                    $table['l'][$j] = $c['miw'];\n                                }\n                            }\n                        }\n                        if ($c['miw'] > $wc['miw']) {\n                            $wc['miw'] = $c['miw'];\n                        }\n                        if ($wc['miw'] > $wc['maw']) {\n                            $wc['maw'] = $wc['miw'];\n                        }\n                        continue;\n                    }\n\n                    if ($firstpass) {\n                        if (isset($c['s'])) {\n                            $c['s'] += $extrcw;\n                        }\n                        if (isset($c['maxs'])) {\n                            $c['maxs'] += $extrcw;\n                        }\n                        if (isset($c['nestedmiw'])) {\n                            $c['nestedmiw'] += $extrcw;\n                        }\n                        if (isset($c['nestedmaw'])) {\n                            $c['nestedmaw'] += $extrcw;\n                        }\n                    }\n\n\n                    // If minimum width has already been set by a nested table or inline object (image/form), use it\n                    if (isset($c['nestedmiw']) && (!isset($this->table[1][1]['overflow']) || $this->table[1][1]['overflow'] != 'visible')) {\n                        $miw = $c['nestedmiw'];\n                    } else {\n                        $miw = $mw;\n                    }\n\n                    if (isset($c['maxs']) && $c['maxs'] != '') {\n                        $c['s'] = $c['maxs'];\n                    }\n\n                    // If maximum width has already been set by a nested table, use it\n                    if (isset($c['nestedmaw'])) {\n                        $c['maw'] = $c['nestedmaw'];\n                    } else {\n                        $c['maw'] = $c['s'];\n                    }\n\n                    if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {\n                        if (($c['maw'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {\n                            $c['maw'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;\n                        }\n                    }\n\n                    if (isset($c['nowrap']) && $c['nowrap']) {\n                        $miw = $c['maw'];\n                    }\n\n                    if (isset($c['wpercent']) && $firstpass) {\n                        if (isset($c['colspan'])) { // Not perfect - but % set on colspan is shared equally on cols.\n                            for ($k = 0; $k < $c['colspan']; $k++) {\n                                $table['wc'][($j + $k)]['wpercent'] = $c['wpercent'] / $c['colspan'];\n                            }\n                        } else {\n                            if (isset($table['w']) && $table['w']) {\n                                $c['w'] = $c['wpercent'] / 100 * ($table['w'] - $tblbw );\n                            }\n                            $wc['wpercent'] = $c['wpercent'];\n                        }\n                    }\n\n                    if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {\n                        if (isset($c['w']) && ($c['w'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {\n                            $c['w'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;\n                        }\n                    }\n\n\n                    if (isset($c['w'])) { // If cell width is specified\n                        if ($miw < $c['w']) {\n                            $c['miw'] = $c['w'];\n                        } // Cell min width = that specified\n                        if ($miw > $c['w']) {\n                            $c['miw'] = $c['w'] = $miw;\n                        } // If width specified is less than minimum allowed (W) increase it\n                        // mPDF 5.7.4  Do not set column width in colspan\n                        // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug\n                        if (!isset($c['colspan'])) {\n                            if (!isset($wc['w'])) {\n                                $wc['w'] = 1;\n                            }  // If the Col width is not specified = set it to 1\n                        }\n                        // mPDF 5.7.3  cf. http://www.mpdf1.com/forum/discussion/1648/nested-table-bug-\n                        $c['maw'] = $c['w'];\n                    } else {\n                        $c['miw'] = $miw;\n                    } // If cell width not specified -> set Cell min width it to minimum allowed (W)\n\n                    if (isset($c['miw']) && $c['maw'] < $c['miw']) {\n                        $c['maw'] = $c['miw'];\n                    } // If Cell max width < Minwidth - increase it to =\n                    if (!isset($c['colspan'])) {\n                        if (isset($c['miw']) && $wc['miw'] < $c['miw']) {\n                            $wc['miw'] = $c['miw'];\n                        } // Update Col Minimum and maximum widths\n                        if ($wc['maw'] < $c['maw']) {\n                            $wc['maw'] = $c['maw'];\n                        }\n                        if ((isset($wc['absmiw']) && $wc['absmiw'] < $c['absmiw']) || !isset($wc['absmiw'])) {\n                            $wc['absmiw'] = $c['absmiw'];\n                        } // Update Col Minimum and maximum widths\n\n                        if (isset($table['l'][$j])) {\n                            $table['l'][$j] += $c['s'];\n                        } else {\n                            $table['l'][$j] = $c['s'];\n                        }\n                    } else {\n                        $listspan[] = [$i, $j];\n                    }\n\n                    // Check if minimum width of the whole column is big enough for largest word to fit\n                    // mPDF 6\n                    if (isset($c['textbuffer'])) {\n                        if (isset($table['overflow']) && $table['overflow'] == 'wrap') {\n                            $letter = true;\n                        } // check for maximum width of letters\n                        else {\n                            $letter = false;\n                        }\n                        $minwidth = $this->TableCheckMinWidth($wc['miw'] - $extrcw, 0, $c['textbuffer'], $letter);\n                    } else {\n                        $minwidth = 0;\n                    }\n                    if ($minwidth < 0) {\n                        // increase minimum width\n                        if (!isset($c['colspan'])) {\n                            $wc['miw'] = max((isset($wc['miw']) ? $wc['miw'] : 0), ((-$minwidth) + $extrcw));\n                        } else {\n                            $c['miw'] = max((isset($c['miw']) ? $c['miw'] : 0), ((-$minwidth) + $extrcw));\n                        }\n                    }\n                    if (!isset($c['colspan'])) {\n                        if ($wc['miw'] > $wc['maw']) {\n                            $wc['maw'] = $wc['miw'];\n                        } // update maximum width, if needed\n                    }\n                }\n                unset($c);\n            }//rows\n        }//columns\n        // COLUMN SPANS\n        $wc = &$table['wc'];\n        foreach ($listspan as $span) {\n            [$i, $j] = $span;\n            $c = &$cs[$i][$j];\n            $lc = $j + $c['colspan'];\n            if ($lc > $nc) {\n                $lc = $nc;\n            }\n            $wis = $wisa = 0;\n            $was = $wasa = 0;\n            $list = [];\n            for ($k = $j; $k < $lc; $k++) {\n                if (isset($table['l'][$k])) {\n                    if ($c['R']) {\n                        $table['l'][$k] += $c['miw'] / $c['colspan'];\n                    } else {\n                        $table['l'][$k] += $c['s'] / $c['colspan'];\n                    }\n                } else {\n                    if ($c['R']) {\n                        $table['l'][$k] = $c['miw'] / $c['colspan'];\n                    } else {\n                        $table['l'][$k] = $c['s'] / $c['colspan'];\n                    }\n                }\n                $wis += $wc[$k]['miw'];   // $wis is the sum of the column miw in the colspan\n                $was += $wc[$k]['maw'];   // $was is the sum of the column maw in the colspan\n                if (!isset($c['w'])) {\n                    $list[] = $k;\n                    $wisa += $wc[$k]['miw']; // $wisa is the sum of the column miw in cells with no width specified in the colspan\n                    $wasa += $wc[$k]['maw']; // $wasa is the sum of the column maw in cells with no width specified in the colspan\n                }\n            }\n            if ($c['miw'] > $wis) {\n                if (!$wis) {\n                    for ($k = $j; $k < $lc; $k++) {\n                        $wc[$k]['miw'] = $c['miw'] / $c['colspan'];\n                    }\n                } elseif (!count($list) && $wis != 0) {\n                    $wi = $c['miw'] - $wis;\n                    for ($k = $j; $k < $lc; $k++) {\n                        $wc[$k]['miw'] += ($wc[$k]['miw'] / $wis) * $wi;\n                    }\n                } else {\n                    $wi = $c['miw'] - $wis;\n                    // mPDF 5.7.2   Extra min width distributed proportionately to all cells in colspan without a specified width\n                    // cf. http://www.mpdf1.com/forum/discussion/1607#Item_4\n                    foreach ($list as $k) {\n                        if (!isset($wc[$k]['w']) || !$wc[$k]['w']) {\n                            $wc[$k]['miw'] += ($wc[$k]['miw'] / $wisa) * $wi;\n                        }\n                    } // mPDF 5.7.2\n                }\n            }\n            if ($c['maw'] > $was) {\n                if (!$wis) {\n                    for ($k = $j; $k < $lc; $k++) {\n                        $wc[$k]['maw'] = $c['maw'] / $c['colspan'];\n                    }\n                } elseif (!count($list) && $was != 0) {\n                    $wi = $c['maw'] - $was;\n                    for ($k = $j; $k < $lc; $k++) {\n                        $wc[$k]['maw'] += ($wc[$k]['maw'] / $was) * $wi;\n                    }\n                } else {\n                    $wi = $c['maw'] - $was;\n                    // mPDF 5.7.4  Extra max width distributed evenly to all cells in colspan without a specified width\n                    // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug\n                    foreach ($list as $k) {\n                        $wc[$k]['maw'] += $wi / count($list);\n                    }\n                }\n            }\n            unset($c);\n        }\n\n        $checkminwidth = 0;\n        $checkmaxwidth = 0;\n        $totallength = 0;\n\n        for ($i = 0; $i < $nc; $i++) {\n            $checkminwidth += $table['wc'][$i]['miw'];\n            $checkmaxwidth += $table['wc'][$i]['maw'];\n            $totallength += isset($table['l']) ? $table['l'][$i] : 0;\n        }\n\n        if (!isset($table['w']) && $firstpass) {\n            $sumpc = 0;\n            $notset = 0;\n            for ($i = 0; $i < $nc; $i++) {\n                if (isset($table['wc'][$i]['wpercent']) && $table['wc'][$i]['wpercent']) {\n                    $sumpc += $table['wc'][$i]['wpercent'];\n                } else {\n                    $notset++;\n                }\n            }\n\n            // If sum of widths as %  >= 100% and not all columns are set\n            // Set a nominal width of 1% for unset columns\n            if ($sumpc >= 100 && $notset) {\n                for ($i = 0; $i < $nc; $i++) {\n                    if ((!isset($table['wc'][$i]['wpercent']) || !$table['wc'][$i]['wpercent']) &&\n                        (!isset($table['wc'][$i]['w']) || !$table['wc'][$i]['w'])) {\n                        $table['wc'][$i]['wpercent'] = 1;\n                    }\n                }\n            }\n\n\n            if ($sumpc) { // if any percents are set\n                $sumnonpc = (100 - $sumpc);\n                $sumpc = max($sumpc, 100);\n                $miwleft = 0;\n                $miwleftcount = 0;\n                $miwsurplusnonpc = 0;\n                $maxcalcmiw = 0;\n                $mawleft = 0;\n                $mawleftcount = 0;\n                $mawsurplusnonpc = 0;\n                $maxcalcmaw = 0;\n                $mawnon = 0;\n                $miwnon = 0;\n                for ($i = 0; $i < $nc; $i++) {\n                    if (isset($table['wc'][$i]['wpercent'])) {\n                        $maxcalcmiw = max($maxcalcmiw, ($table['wc'][$i]['miw'] * $sumpc / $table['wc'][$i]['wpercent']));\n                        $maxcalcmaw = max($maxcalcmaw, ($table['wc'][$i]['maw'] * $sumpc / $table['wc'][$i]['wpercent']));\n                    } else {\n                        $miwleft += $table['wc'][$i]['miw'];\n                        $mawleft += $table['wc'][$i]['maw'];\n                        if (!isset($table['wc'][$i]['w'])) {\n                            $miwleftcount++;\n                            $mawleftcount++;\n                        }\n                    }\n                }\n                if ($miwleft && $sumnonpc > 0) {\n                    $miwnon = $miwleft * 100 / $sumnonpc;\n                }\n                if ($mawleft && $sumnonpc > 0) {\n                    $mawnon = $mawleft * 100 / $sumnonpc;\n                }\n                if (($miwnon > $checkminwidth || $maxcalcmiw > $checkminwidth) && $this->keep_table_proportions) {\n                    if ($miwnon > $maxcalcmiw) {\n                        $miwsurplusnonpc = round((($miwnon * $sumnonpc / 100) - $miwleft), 3);\n                        $checkminwidth = $miwnon;\n                    } else {\n                        $checkminwidth = $maxcalcmiw;\n                    }\n                    for ($i = 0; $i < $nc; $i++) {\n                        if (isset($table['wc'][$i]['wpercent'])) {\n                            $newmiw = $checkminwidth * $table['wc'][$i]['wpercent'] / 100;\n                            if ($table['wc'][$i]['miw'] < $newmiw) {\n                                $table['wc'][$i]['miw'] = $newmiw;\n                            }\n                            $table['wc'][$i]['w'] = 1;\n                        } elseif ($miwsurplusnonpc && !$table['wc'][$i]['w']) {\n                            $table['wc'][$i]['miw'] += $miwsurplusnonpc / $miwleftcount;\n                        }\n                    }\n                }\n                if (($mawnon > $checkmaxwidth || $maxcalcmaw > $checkmaxwidth)) {\n                    if ($mawnon > $maxcalcmaw) {\n                        $mawsurplusnonpc = round((($mawnon * $sumnonpc / 100) - $mawleft), 3);\n                        $checkmaxwidth = $mawnon;\n                    } else {\n                        $checkmaxwidth = $maxcalcmaw;\n                    }\n                    for ($i = 0; $i < $nc; $i++) {\n                        if (isset($table['wc'][$i]['wpercent'])) {\n                            $newmaw = $checkmaxwidth * $table['wc'][$i]['wpercent'] / 100;\n                            if ($table['wc'][$i]['maw'] < $newmaw) {\n                                $table['wc'][$i]['maw'] = $newmaw;\n                            }\n                            $table['wc'][$i]['w'] = 1;\n                        } elseif ($mawsurplusnonpc && !$table['wc'][$i]['w']) {\n                            $table['wc'][$i]['maw'] += $mawsurplusnonpc / $mawleftcount;\n                        }\n                        if ($table['wc'][$i]['maw'] < $table['wc'][$i]['miw']) {\n                            $table['wc'][$i]['maw'] = $table['wc'][$i]['miw'];\n                        }\n                    }\n                }\n                if ($checkminwidth > $checkmaxwidth) {\n                    $checkmaxwidth = $checkminwidth;\n                }\n            }\n        }\n\n        if (isset($table['wpercent']) && $table['wpercent']) {\n            $checkminwidth *= (100 / $table['wpercent']);\n            $checkmaxwidth *= (100 / $table['wpercent']);\n        }\n\n\n        $checkminwidth += $tblbw;\n        $checkmaxwidth += $tblbw;\n\n        // Table['miw'] set by percent in first pass may be larger than sum of column miw\n        if ((isset($table['miw']) && $checkminwidth > $table['miw']) || !isset($table['miw'])) {\n            $table['miw'] = $checkminwidth;\n        }\n        if ((isset($table['maw']) && $checkmaxwidth > $table['maw']) || !isset($table['maw'])) {\n            $table['maw'] = $checkmaxwidth;\n        }\n        $table['tl'] = $totallength;\n\n        // mPDF 6\n        if ($this->table_rotate) {\n            $mxw = $this->tbrot_maxw;\n        } else {\n            $mxw = $this->blk[$this->blklvl]['inner_width'];\n        }\n\n        if (!isset($table['overflow'])) {\n            $table['overflow'] = null;\n        }\n\n        if ($table['overflow'] == 'visible') {\n            return [0, 0];\n        } elseif ($table['overflow'] == 'hidden' && !$this->table_rotate && !$this->ColActive && $checkminwidth > $mxw) {\n            $table['w'] = $table['miw'];\n            return [0, 0];\n        }\n        // elseif ($table['overflow']=='wrap') { return array(0,0); }\t// mPDF 6\n\n        if (isset($table['w']) && $table['w']) {\n\n            if ($table['w'] >= $checkminwidth && $table['w'] <= $mxw) {\n                $table['maw'] = $mxw = $table['w'];\n            } elseif ($table['w'] >= $checkminwidth && $table['w'] > $mxw && $this->keep_table_proportions) {\n                $checkminwidth = $table['w'];\n            } elseif ($table['w'] < $checkminwidth && $checkminwidth < $mxw && $this->keep_table_proportions) {\n                $table['maw'] = $table['w'] = $checkminwidth;\n            } else {\n                unset($table['w']);\n            }\n        }\n\n        $ratio = $checkminwidth / $mxw;\n\n        if ($checkminwidth > $mxw) {\n            return [($ratio + 0.001), $checkminwidth]; // 0.001 to allow for rounded numbers when resizing\n        }\n\n        unset($cs);\n\n        return [0, 0];\n    }\n\n    function _tableWidth(&$table)\n    {\n        $widthcols = &$table['wc'];\n        $numcols = $table['nc'];\n        $tablewidth = 0;\n\n        if ($table['borders_separate']) {\n            $tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H'];\n        } else {\n            $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];\n        }\n\n        if ($table['level'] > 1 && isset($table['w'])) {\n\n            if (isset($table['wpercent']) && $table['wpercent']) {\n                $table['w'] = $temppgwidth = (($table['w'] - $tblbw) * $table['wpercent'] / 100) + $tblbw;\n            } else {\n                $temppgwidth = $table['w'];\n            }\n\n        } elseif ($this->table_rotate) {\n\n            $temppgwidth = $this->tbrot_maxw;\n\n            // If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin) then allow for this\n            $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];\n\n            if ($enddiv / $temppgwidth < 0.05) {\n                $temppgwidth -= $enddiv;\n            }\n\n        } else {\n\n            if (isset($table['w']) && $table['w'] < $this->blk[$this->blklvl]['inner_width']) {\n                $notfullwidth = 1;\n                $temppgwidth = $table['w'];\n            } elseif ($table['overflow'] == 'visible' && $table['level'] == 1) {\n                $temppgwidth = null;\n            } elseif ($table['overflow'] == 'hidden' && !$this->ColActive && isset($table['w']) && $table['w'] > $this->blk[$this->blklvl]['inner_width'] && $table['w'] == $table) {\n                // $temppgwidth = $this->blk[$this->blklvl]['inner_width'];\n                $temppgwidth = $table['w'];\n            } else {\n                $temppgwidth = $this->blk[$this->blklvl]['inner_width'];\n            }\n\n        }\n\n        $totaltextlength = 0; // Added - to sum $table['l'][colno]\n        $totalatextlength = 0; // Added - to sum $table['l'][colno] for those columns where width not set\n        $percentages_set = 0;\n\n        for ($i = 0; $i < $numcols; $i++) {\n            if (isset($widthcols[$i]['wpercent'])) {\n                $tablewidth += $widthcols[$i]['maw'];\n                $percentages_set = 1;\n            } elseif (isset($widthcols[$i]['w'])) {\n                $tablewidth += $widthcols[$i]['miw'];\n            } else {\n                $tablewidth += $widthcols[$i]['maw'];\n            }\n            $totaltextlength += isset($table['l']) ? $table['l'][$i] : 0;\n        }\n\n        if (!$totaltextlength) {\n            $totaltextlength = 1;\n        }\n\n        $tablewidth += $tblbw; // Outer half of table borders\n\n        if ($tablewidth > $temppgwidth) {\n            $table['w'] = $temppgwidth;\n        } elseif ($tablewidth < $temppgwidth && !isset($table['w']) && $percentages_set) { // if any widths set as percentages and max width fits < page width\n            $table['w'] = $table['maw'];\n        }\n\n        // if table width is set and is > allowed width\n        if (isset($table['w']) && $table['w'] > $temppgwidth) {\n            $table['w'] = $temppgwidth;\n        }\n\n        // IF the table width is now set - Need to distribute columns widths\n        // mPDF 5.7.3\n        // If the table width is already set to the maximum width (e.g. nested table), then use maximum column widths exactly\n        if (isset($table['w']) && ($table['w'] == $tablewidth) && !$percentages_set) {\n\n            // This sets the columns all to maximum width\n            for ($i = 0; $i < $numcols; $i++) {\n                $widthcols[$i] = $widthcols[$i]['maw'];\n            }\n\n        } elseif (isset($table['w'])) { // elseif the table width is set distribute width using algorithm\n\n            $wis = $wisa = 0;\n            $list = [];\n            $notsetlist = [];\n\n            for ($i = 0; $i < $numcols; $i++) {\n                $wis += $widthcols[$i]['miw'];\n                if (!isset($widthcols[$i]['w']) || ($widthcols[$i]['w'] && $table['w'] > $temppgwidth && !$this->keep_table_proportions && !$notfullwidth )) {\n                    $list[] = $i;\n                    $wisa += $widthcols[$i]['miw'];\n                    $totalatextlength += $table['l'][$i];\n                }\n            }\n\n            if (!$totalatextlength) {\n                $totalatextlength = 1;\n            }\n\n            // Allocate spare (more than col's minimum width) across the cols according to their approx total text length\n            // Do it by setting minimum width here\n            if ($table['w'] > $wis + $tblbw) {\n\n                // First set any cell widths set as percentages\n                if ($table['w'] < $temppgwidth || $this->keep_table_proportions) {\n                    for ($k = 0; $k < $numcols; $k++) {\n                        if (isset($widthcols[$k]['wpercent'])) {\n                            $curr = $widthcols[$k]['miw'];\n                            $widthcols[$k]['miw'] = ($table['w'] - $tblbw) * $widthcols[$k]['wpercent'] / 100;\n                            $wis += $widthcols[$k]['miw'] - $curr;\n                            $wisa += $widthcols[$k]['miw'] - $curr;\n                        }\n                    }\n                }\n\n                // Now allocate surplus up to maximum width of each column\n                $surplus = 0;\n                $ttl = 0; // number of surplus columns\n\n                if (!count($list)) {\n\n                    $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute\n\n                    for ($k = 0; $k < $numcols; $k++) {\n\n                        $spareratio = ($table['l'][$k] / $totaltextlength); //  gives ratio to divide up free space\n\n                        // Don't allocate more than Maximum required width - save rest in surplus\n                        if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3\n                            $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);\n                            $widthcols[$k]['miw'] = $widthcols[$k]['maw'];\n                        } else {\n                            $notsetlist[] = $k;\n                            $ttl += $table['l'][$k];\n                            $widthcols[$k]['miw'] += ($wi * $spareratio);\n                        }\n                    }\n\n                } else {\n\n                    $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute\n\n                    foreach ($list as $k) {\n\n                        $spareratio = ($table['l'][$k] / $totalatextlength); //  gives ratio to divide up free space\n\n                        // Don't allocate more than Maximum required width - save rest in surplus\n                        if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3\n                            $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);\n                            $widthcols[$k]['miw'] = $widthcols[$k]['maw'];\n                        } else {\n                            $notsetlist[] = $k;\n                            $ttl += $table['l'][$k];\n                            $widthcols[$k]['miw'] += ($wi * $spareratio);\n                        }\n                    }\n                }\n\n                // If surplus still left over apportion it across columns\n                if ($surplus) {\n\n                    if (count($notsetlist) && count($notsetlist) < $numcols) { // if some are set only add to remaining - otherwise add to all of them\n                        foreach ($notsetlist as $i) {\n                            if ($ttl) {\n                                $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;\n                            }\n                        }\n                    } elseif (count($list) && count($list) < $numcols) { // If some widths are defined, and others have been added up to their maxmum\n                        foreach ($list as $i) {\n                            $widthcols[$i]['miw'] += $surplus / count($list);\n                        }\n                    } elseif ($numcols) { // If all columns\n                        $ttl = array_sum($table['l']);\n                        if ($ttl) {\n                            for ($i = 0; $i < $numcols; $i++) {\n                                $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;\n                            }\n                        }\n                    }\n                }\n            }\n\n            // This sets the columns all to minimum width (which has been increased above if appropriate)\n            for ($i = 0; $i < $numcols; $i++) {\n                $widthcols[$i] = $widthcols[$i]['miw'];\n            }\n\n            // TABLE NOT WIDE ENOUGH EVEN FOR MINIMUM CONTENT WIDTH\n            // If sum of column widths set are too wide for table\n            $checktablewidth = 0;\n            for ($i = 0; $i < $numcols; $i++) {\n                $checktablewidth += $widthcols[$i];\n            }\n\n            if ($checktablewidth > ($temppgwidth + 0.001 - $tblbw)) {\n\n                $usedup = 0;\n                $numleft = 0;\n\n                for ($i = 0; $i < $numcols; $i++) {\n                    if ((isset($widthcols[$i]) && $widthcols[$i] > (($temppgwidth - $tblbw) / $numcols)) && (!isset($widthcols[$i]['w']))) {\n                        $numleft++;\n                        unset($widthcols[$i]);\n                    } else {\n                        $usedup += $widthcols[$i];\n                    }\n                }\n\n                for ($i = 0; $i < $numcols; $i++) {\n                    if (!isset($widthcols[$i]) || !$widthcols[$i]) {\n                        $widthcols[$i] = ((($temppgwidth - $tblbw) - $usedup) / ($numleft));\n                    }\n                }\n            }\n\n        } else { // table has no width defined\n\n            $table['w'] = $tablewidth;\n\n            for ($i = 0; $i < $numcols; $i++) {\n\n                if (isset($widthcols[$i]['wpercent']) && $this->keep_table_proportions) {\n                    $colwidth = $widthcols[$i]['maw'];\n                } elseif (isset($widthcols[$i]['w'])) {\n                    $colwidth = $widthcols[$i]['miw'];\n                } else {\n                    $colwidth = $widthcols[$i]['maw'];\n                }\n\n                unset($widthcols[$i]);\n                $widthcols[$i] = $colwidth;\n\n            }\n        }\n\n        if ($table['overflow'] === 'visible' && $table['level'] == 1) {\n\n            if ($tablewidth > $this->blk[$this->blklvl]['inner_width']) {\n\n                for ($j = 0; $j < $numcols; $j++) { // columns\n\n                    for ($i = 0; $i < $table['nr']; $i++) { // rows\n\n                        if (isset($table['cells'][$i][$j]) && $table['cells'][$i][$j]) {\n\n                            $colspan = (isset($table['cells'][$i][$j]['colspan']) ? $table['cells'][$i][$j]['colspan'] : 1);\n\n                            if ($colspan > 1) {\n                                $w = 0;\n\n                                for ($c = $j; $c < ($j + $colspan); $c++) {\n                                    $w += $widthcols[$c];\n                                }\n\n                                if ($w > $this->blk[$this->blklvl]['inner_width']) {\n                                    $diff = $w - ($this->blk[$this->blklvl]['inner_width'] - $tblbw);\n                                    for ($c = $j; $c < ($j + $colspan); $c++) {\n                                        $widthcols[$c] -= $diff * ($widthcols[$c] / $w);\n                                    }\n                                    $table['w'] -= $diff;\n                                    $table['csp'][$j] = $w - $diff;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            $pgNo = 0;\n            $currWc = 0;\n\n            for ($i = 0; $i < $numcols; $i++) { // columns\n\n                if (isset($table['csp'][$i])) {\n                    $w = $table['csp'][$i];\n                    unset($table['csp'][$i]);\n                } else {\n                    $w = $widthcols[$i];\n                }\n\n                if (($currWc + $w + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {\n                    $pgNo++;\n                    $currWc = $widthcols[$i];\n                } else {\n                    $currWc += $widthcols[$i];\n                }\n\n                $table['colPg'][$i] = $pgNo;\n            }\n        }\n    }\n\n    function _tableHeight(&$table)\n    {\n        $level = $table['level'];\n        $levelid = $table['levelid'];\n        $cells = &$table['cells'];\n        $numcols = $table['nc'];\n        $numrows = $table['nr'];\n        $listspan = [];\n        $checkmaxheight = 0;\n        $headerrowheight = 0;\n        $checkmaxheightplus = 0;\n        $headerrowheightplus = 0;\n        $firstrowheight = 0;\n\n        $footerrowheight = 0;\n        $footerrowheightplus = 0;\n        if ($this->table_rotate) {\n            $temppgheight = $this->tbrot_maxh;\n            $remainingpage = $this->tbrot_maxh;\n        } else {\n            $temppgheight = ($this->h - $this->bMargin - $this->tMargin) - $this->kwt_height;\n            $remainingpage = ($this->h - $this->bMargin - $this->y) - $this->kwt_height;\n\n            // If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin)\n            // then allow for this\n            $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $table['margin']['B'];\n            if ($remainingpage > $enddiv && $enddiv / $remainingpage < 0.05) {\n                $remainingpage -= $enddiv;\n            } elseif ($remainingpage == 0) {\n                $remainingpage = 0.001;\n            }\n            if ($temppgheight > $enddiv && $enddiv / $temppgheight < 0.05) {\n                $temppgheight -= $enddiv;\n            } elseif ($temppgheight == 0) {\n                $temppgheight = 0.001;\n            }\n        }\n        if ($remainingpage < 0) {\n            $remainingpage = 0.001;\n        }\n        if ($temppgheight < 0) {\n            $temppgheight = 0.001;\n        }\n\n        for ($i = 0; $i < $numrows; $i++) { // rows\n            $heightrow = &$table['hr'][$i];\n            for ($j = 0; $j < $numcols; $j++) { // columns\n                if (isset($cells[$i][$j]) && $cells[$i][$j]) {\n                    $c = &$cells[$i][$j];\n\n                    if ($this->simpleTables) {\n                        if ($table['borders_separate']) { // NB twice border width\n                            $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) + ($c['padding']['L'] + $c['padding']['R']) + $table['border_spacing_H'];\n                            $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) + ($c['padding']['T'] + $c['padding']['B']) + $table['border_spacing_V'];\n                        } else {\n                            $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + ($c['padding']['L'] + $c['padding']['R']);\n                            $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) / 2 + ($c['padding']['T'] + $c['padding']['B']);\n                        }\n                    } else {\n                        if ($this->packTableData) {\n                            [$bt, $br, $bb, $bl] = $this->_getBorderWidths($c['borderbin']);\n                        } else {\n                            $bt = $c['border_details']['T']['w'];\n                            $bb = $c['border_details']['B']['w'];\n                            $br = $c['border_details']['R']['w'];\n                            $bl = $c['border_details']['L']['w'];\n                        }\n                        if ($table['borders_separate']) { // NB twice border width\n                            $extraWLR = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];\n                            $extrh = $bt + $bb + $c['padding']['T'] + $c['padding']['B'] + $table['border_spacing_V'];\n                        } else {\n                            $extraWLR = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];\n                            $extrh = $bt / 2 + $bb / 2 + $c['padding']['T'] + $c['padding']['B'];\n                        }\n                    }\n\n                    if ($table['overflow'] == 'visible' && $level == 1) {\n                        [$x, $cw] = $this->_splitTableGetWidth($table, $i, $j);\n                    } else {\n                        [$x, $cw] = $this->_tableGetWidth($table, $i, $j);\n                    }\n\n\n                    // Get CELL HEIGHT\n                    // ++ extra parameter forces wrap to break word\n                    if ($c['R'] && isset($c['textbuffer'])) {\n                        $str = '';\n                        foreach ($c['textbuffer'] as $t) {\n                            $str .= $t[0] . ' ';\n                        }\n                        $str = rtrim($str);\n                        $s_fs = $this->FontSizePt;\n                        $s_f = $this->FontFamily;\n                        $s_st = $this->FontStyle;\n                        $this->SetFont($c['textbuffer'][0][4], $c['textbuffer'][0][2], $c['textbuffer'][0][11] / $this->shrin_k, true, true);\n                        $tempch = $this->GetStringWidth($str, true, $c['textbuffer'][0][18], $c['textbuffer'][0][8]);\n                        if ($c['R'] >= 45 && $c['R'] < 90) {\n                            $tempch = ((sin(deg2rad($c['R']))) * $tempch ) + ((sin(deg2rad($c['R']))) * (($c['textbuffer'][0][11] / Mpdf::SCALE) / $this->shrin_k));\n                        }\n                        $this->SetFont($s_f, $s_st, $s_fs, true, true);\n                        $ch = ($tempch ) + $extrh;\n                    } else {\n                        if (isset($c['textbuffer']) && !empty($c['textbuffer'])) {\n                            $this->cellLineHeight = $c['cellLineHeight'];\n                            $this->cellLineStackingStrategy = $c['cellLineStackingStrategy'];\n                            $this->cellLineStackingShift = $c['cellLineStackingShift'];\n                            $this->divwidth = $cw - $extraWLR;\n                            $tempch = $this->printbuffer($c['textbuffer'], '', true, true);\n                        } else {\n                            $tempch = 0;\n                        }\n\n                        // Added cellpadding top and bottom. (Lineheight already adjusted)\n                        $ch = $tempch + $extrh;\n                    }\n                    // If height is defined and it is bigger than calculated $ch then update values\n                    if (isset($c['h']) && $c['h'] > $ch) {\n                        $c['mih'] = $ch; // in order to keep valign working\n                        $ch = $c['h'];\n                    } else {\n                        $c['mih'] = $ch;\n                    }\n                    if (isset($c['rowspan'])) {\n                        $listspan[] = [$i, $j];\n                    } elseif ($heightrow < $ch) {\n                        $heightrow = $ch;\n                    }\n\n                    // this is the extra used in _tableWrite to determine whether to trigger a page change\n                    if ($table['borders_separate']) {\n                        if ($i == ($numrows - 1) || (isset($c['rowspan']) && ($i + $c['rowspan']) == ($numrows))) {\n                            $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;\n                        } else {\n                            $extra = $table['border_spacing_V'] / 2;\n                        }\n                    } else {\n                        if (!$this->simpleTables) {\n                            $extra = $bb / 2;\n                        } elseif ($this->simpleTables) {\n                            $extra = $table['simple']['border_details']['B']['w'] / 2;\n                        }\n                    }\n                    if (isset($table['is_thead'][$i]) && $table['is_thead'][$i]) {\n                        if ($j == 0) {\n                            $headerrowheight += $ch;\n                            $headerrowheightplus += $ch + $extra;\n                        }\n                    } elseif (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {\n                        if ($j == 0) {\n                            $footerrowheight += $ch;\n                            $footerrowheightplus += $ch + $extra;\n                        }\n                    } else {\n                        $checkmaxheight = max($checkmaxheight, $ch);\n                        $checkmaxheightplus = max($checkmaxheightplus, $ch + $extra);\n                    }\n                    if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {\n                        $firstrowheight = max($ch, $firstrowheight);\n                    }\n                    unset($c);\n                }\n            }//end of columns\n        }//end of rows\n\n        $heightrow = &$table['hr'];\n        foreach ($listspan as $span) {\n            [$i, $j] = $span;\n            $c = &$cells[$i][$j];\n            $lr = $i + $c['rowspan'];\n            if ($lr > $numrows) {\n                $lr = $numrows;\n            }\n            $hs = $hsa = 0;\n            $list = [];\n            for ($k = $i; $k < $lr; $k++) {\n                $hs += $heightrow[$k];\n                // mPDF 6\n                $sh = false; // specified height\n                for ($m = 0; $m < $numcols; $m++) { // columns\n                    $tc = &$cells[$k][$m];\n                    if (isset($tc['rowspan'])) {\n                        continue;\n                    }\n                    if (isset($tc['h'])) {\n                        $sh = true;\n                        break;\n                    }\n                }\n                if (!$sh) {\n                    $list[] = $k;\n                }\n            }\n\n            if ($table['borders_separate']) {\n                if ($i == ($numrows - 1) || ($i + $c['rowspan']) == ($numrows)) {\n                    $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;\n                } else {\n                    $extra = $table['border_spacing_V'] / 2;\n                }\n            } else {\n                if (!$this->simpleTables) {\n                    if ($this->packTableData) {\n                        [$bt, $br, $bb, $bl] = $this->_getBorderWidths($c['borderbin']);\n                    } else {\n                        $bb = $c['border_details']['B']['w'];\n                    }\n                    $extra = $bb / 2;\n                } elseif ($this->simpleTables) {\n                    $extra = $table['simple']['border_details']['B']['w'] / 2;\n                }\n            }\n            if (!empty($table['is_thead'][$i])) {\n                $headerrowheight = max($headerrowheight, $hs);\n                $headerrowheightplus = max($headerrowheightplus, $hs + $extra);\n            } elseif (!empty($table['is_tfoot'][$i])) {\n                $footerrowheight = max($footerrowheight, $hs);\n                $footerrowheightplus = max($footerrowheightplus, $hs + $extra);\n            } else {\n                $checkmaxheight = max($checkmaxheight, $hs);\n                $checkmaxheightplus = max($checkmaxheightplus, $hs + $extra);\n            }\n            if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {\n                $firstrowheight = max($hs, $firstrowheight);\n            }\n\n            if ($c['mih'] > $hs) {\n                if (!$hs) {\n                    for ($k = $i; $k < $lr; $k++) {\n                        $heightrow[$k] = $c['mih'] / $c['rowspan'];\n                    }\n                } elseif (!count($list)) { // no rows in the rowspan have a height specified, so share amongst all rows equally\n                    $hi = $c['mih'] - $hs;\n                    for ($k = $i; $k < $lr; $k++) {\n                        $heightrow[$k] += ($heightrow[$k] / $hs) * $hi;\n                    }\n                } else {\n                    $hi = $c['mih'] - $hs; // mPDF 6\n                    foreach ($list as $k) {\n                        $heightrow[$k] += $hi / (count($list)); // mPDF 6\n                    }\n                }\n            }\n            unset($c);\n\n            // If rowspans overlap so that one or more rows do not have a height set...\n            // i.e. for one or more rows, the only cells (explicit) in that row have rowspan>1\n            // so heightrow is still == 0\n            if ($heightrow[$i] == 0) {\n                // Get row extent to analyse above and below\n                $top = $i;\n                foreach ($listspan as $checkspan) {\n                    [$cki, $ckj] = $checkspan;\n                    $c = &$cells[$cki][$ckj];\n                    if (isset($c['rowspan']) && $c['rowspan'] > 1) {\n                        if (($cki + $c['rowspan'] - 1) >= $i) {\n                            $top = min($top, $cki);\n                        }\n                    }\n                }\n                $bottom = $i + $c['rowspan'] - 1;\n                // Check for overconstrained conditions\n                for ($k = $top; $k <= $bottom; $k++) {\n                    // if ['hr'] for any of the others is also 0, then abort (too complicated)\n                    if ($k != $i && $heightrow[$k] == 0) {\n                        break(1);\n                    }\n                    // check again that top and bottom are not crossed by rowspans - or abort (too complicated)\n                    if ($k == $top) {\n                        // ???? take account of colspan as well???\n                        for ($m = 0; $m < $numcols; $m++) { // columns\n                            if (!isset($cells[$k][$m]) || $cells[$k][$m] == 0) {\n                                break(2);\n                            }\n                        }\n                    } elseif ($k == $bottom) {\n                        // ???? take account of colspan as well???\n                        for ($m = 0; $m < $numcols; $m++) { // columns\n                            $c = &$cells[$k][$m];\n                            if (isset($c['rowspan']) && $c['rowspan'] > 1) {\n                                break(2);\n                            }\n                        }\n                    }\n                }\n                // By columns add up col height using ['h'] if set or ['mih'] if not\n                // Intentionally do not substract border-spacing\n                $colH = [];\n                $extH = 0;\n                $newhr = [];\n                for ($m = 0; $m < $numcols; $m++) { // columns\n                    for ($k = $top; $k <= $bottom; $k++) {\n                        if (isset($cells[$k][$m]) && $cells[$k][$m] != 0) {\n                            $c = &$cells[$k][$m];\n                            if (isset($c['h']) && $c['h']) {\n                                $useh = $c['h'];\n                            } // ???? take account of colspan as well???\n                            else {\n                                $useh = $c['mih'];\n                            }\n                            if (isset($colH[$m])) {\n                                $colH[$m] += $useh;\n                            } else {\n                                $colH[$m] = $useh;\n                            }\n                            if (!isset($c['rowspan']) || $c['rowspan'] < 2) {\n                                $newhr[$k] = max((isset($newhr[$k]) ? $newhr[$k] : 0), $useh);\n                            }\n                        }\n                    }\n                    $extH = max($extH, $colH[$m]); // mPDF 6\n                }\n                $newhr[$i] = $extH - array_sum($newhr);\n                for ($k = $top; $k <= $bottom; $k++) {\n                    $heightrow[$k] = $newhr[$k];\n                }\n            }\n\n\n            unset($c);\n        }\n\n        $table['h'] = array_sum($heightrow);\n        unset($heightrow);\n\n        if ($table['borders_separate']) {\n            $table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['border_details']['T']['w'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] + $table['padding']['T'] + $table['padding']['B'];\n        } else {\n            $table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['max_cell_border_width']['T'] / 2 + $table['max_cell_border_width']['B'] / 2;\n        }\n\n        $maxrowheight = $checkmaxheightplus + $headerrowheightplus + $footerrowheightplus;\n        $maxfirstrowheight = $firstrowheight + $headerrowheightplus + $footerrowheightplus; // includes thead, 1st row and tfoot\n        return [$table['h'], $maxrowheight, $temppgheight, $remainingpage, $maxfirstrowheight];\n    }\n\n    function _tableGetWidth(&$table, $i, $j)\n    {\n        $cell = &$table['cells'][$i][$j];\n        if ($cell) {\n            if (isset($cell['x0'])) {\n                return [$cell['x0'], $cell['w0']];\n            }\n            $x = 0;\n            $widthcols = &$table['wc'];\n            for ($k = 0; $k < $j; $k++) {\n                $x += $widthcols[$k];\n            }\n            $w = $widthcols[$j];\n            if (isset($cell['colspan'])) {\n                for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {\n                    $w += $widthcols[$k];\n                }\n            }\n            $cell['x0'] = $x;\n            $cell['w0'] = $w;\n            return [$x, $w];\n        }\n        return [0, 0];\n    }\n\n    function _splitTableGetWidth(&$table, $i, $j)\n    {\n        $cell = &$table['cells'][$i][$j];\n        if ($cell) {\n            if (isset($cell['x0'])) {\n                return [$cell['x0'], $cell['w0']];\n            }\n            $x = 0;\n            $widthcols = &$table['wc'];\n            $pg = $table['colPg'][$j];\n            for ($k = 0; $k < $j; $k++) {\n                if ($table['colPg'][$k] == $pg) {\n                    $x += $widthcols[$k];\n                }\n            }\n            $w = $widthcols[$j];\n            if (isset($cell['colspan'])) {\n                for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--) {\n                    if ($table['colPg'][$k] == $pg) {\n                        $w += $widthcols[$k];\n                    }\n                }\n            }\n            $cell['x0'] = $x;\n            $cell['w0'] = $w;\n            return [$x, $w];\n        }\n        return [0, 0];\n    }\n\n    function _tableGetHeight(&$table, $i, $j)\n    {\n        $cell = &$table['cells'][$i][$j];\n        if ($cell) {\n            if (isset($cell['y0'])) {\n                return [$cell['y0'], $cell['h0']];\n            }\n            $y = 0;\n            $heightrow = &$table['hr'];\n            for ($k = 0; $k < $i; $k++) {\n                $y += $heightrow[$k];\n            }\n            $h = $heightrow[$i];\n            if (isset($cell['rowspan'])) {\n                for ($k = $i + $cell['rowspan'] - 1; $k > $i; $k--) {\n                    if (array_key_exists($k, $heightrow)) {\n                        $h += $heightrow[$k];\n                    } else {\n                        $this->logger->debug('Possible non-wellformed HTML markup in a table', ['context' => LogContext::HTML_MARKUP]);\n                    }\n                }\n            }\n            $cell['y0'] = $y;\n            $cell['h0'] = $h;\n            return [$y, $h];\n        }\n        return [0, 0];\n    }\n\n    function _tableGetMaxRowHeight($table, $row)\n    {\n        if ($row == $table['nc'] - 1) {\n            return $table['hr'][$row];\n        }\n        $maxrowheight = $table['hr'][$row];\n        for ($i = $row + 1; $i < $table['nr']; $i++) {\n            $cellsset = 0;\n            for ($j = 0; $j < $table['nc']; $j++) {\n                if ($table['cells'][$i][$j]) {\n                    if (isset($table['cells'][$i][$j]['colspan'])) {\n                        $cellsset += $table['cells'][$i][$j]['colspan'];\n                    } else {\n                        $cellsset += 1;\n                    }\n                }\n            }\n            if ($cellsset == $table['nc']) {\n                return $maxrowheight;\n            } else {\n                $maxrowheight += $table['hr'][$i];\n            }\n        }\n        return $maxrowheight;\n    }\n\n    // CHANGED TO ALLOW TABLE BORDER TO BE SPECIFIED CORRECTLY - added border_details\n    function _tableRect($x, $y, $w, $h, $bord = -1, $details = [], $buffer = false, $bSeparate = false, $cort = 'cell', $tablecorner = '', $bsv = 0, $bsh = 0)\n    {\n        $cellBorderOverlay = [];\n\n        if ($bord == -1) {\n            $this->Rect($x, $y, $w, $h);\n        } elseif ($this->simpleTables && ($cort == 'cell')) {\n            $this->SetLineWidth($details['L']['w']);\n            if ($details['L']['c']) {\n                $this->SetDColor($details['L']['c']);\n            } else {\n                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            }\n            $this->SetLineJoin(0);\n            $this->Rect($x, $y, $w, $h);\n        } elseif ($bord) {\n            if (!$bSeparate && $buffer) {\n                $priority = 'LRTB';\n                for ($p = 0; $p < strlen($priority); $p++) {\n                    $side = $priority[$p];\n                    $details['p'] = $side;\n\n                    $dom = 0;\n                    if (isset($details[$side]['w'])) {\n                        $dom += ($details[$side]['w'] * 100000);\n                    }\n                    if (isset($details[$side]['style'])) {\n                        $dom += (array_search($details[$side]['style'], $this->borderstyles) * 100);\n                    }\n                    if (isset($details[$side]['dom'])) {\n                        $dom += ($details[$side]['dom'] * 10);\n                    }\n\n                    // Precedence to darker colours at joins\n                    $coldom = 0;\n                    if (isset($details[$side]['c']) && is_array($details[$side]['c'])) {\n                        if ($details[$side]['c'][0] == 3) {  // RGB\n                            $coldom = 10 - (((ord($details[$side]['c'][1]) * 1.00) + (ord($details[$side]['c'][2]) * 1.00) + (ord($details[$side]['c'][3]) * 1.00)) / 76.5);\n                        }\n                    } // 10 black - 0 white\n                    if ($coldom) {\n                        $dom += $coldom;\n                    }\n                    // Lastly precedence to RIGHT and BOTTOM cells at joins\n                    if (isset($details['cellposdom'])) {\n                        $dom += $details['cellposdom'];\n                    }\n\n                    $save = false;\n                    if ($side == 'T' && $this->issetBorder($bord, Border::TOP)) {\n                        $cbord = Border::TOP;\n                        $save = true;\n                    } elseif ($side == 'L' && $this->issetBorder($bord, Border::LEFT)) {\n                        $cbord = Border::LEFT;\n                        $save = true;\n                    } elseif ($side == 'R' && $this->issetBorder($bord, Border::RIGHT)) {\n                        $cbord = Border::RIGHT;\n                        $save = true;\n                    } elseif ($side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) {\n                        $cbord = Border::BOTTOM;\n                        $save = true;\n                    }\n\n                    if ($save) {\n                        $this->cellBorderBuffer[] = pack(\"A16nCnda6A10d14\", str_pad(sprintf(\"%08.7f\", $dom), 16, \"0\", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 0);\n                        if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset' || $details[$side]['style'] == 'double') {\n                            $details[$side]['overlay'] = true;\n                            $this->cellBorderBuffer[] = pack(\"A16nCnda6A10d14\", str_pad(sprintf(\"%08.7f\", ($dom + 4)), 16, \"0\", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 1);\n                        }\n                    }\n                }\n                return;\n            }\n\n            if (isset($details['p']) && strlen($details['p']) > 1) {\n                $priority = $details['p'];\n            } else {\n                $priority = 'LTRB';\n            }\n            $Tw = 0;\n            $Rw = 0;\n            $Bw = 0;\n            $Lw = 0;\n            if (isset($details['T']['w'])) {\n                $Tw = $details['T']['w'];\n            }\n            if (isset($details['R']['w'])) {\n                $Rw = $details['R']['w'];\n            }\n            if (isset($details['B']['w'])) {\n                $Bw = $details['B']['w'];\n            }\n            if (isset($details['L']['w'])) {\n                $Lw = $details['L']['w'];\n            }\n\n            $x2 = $x + $w;\n            $y2 = $y + $h;\n            $oldlinewidth = $this->LineWidth;\n\n            for ($p = 0; $p < strlen($priority); $p++) {\n                $side = $priority[$p];\n                $xadj = 0;\n                $xadj2 = 0;\n                $yadj = 0;\n                $yadj2 = 0;\n                $print = false;\n                if ($Tw && $side == 'T' && $this->issetBorder($bord, Border::TOP)) { // TOP\n                    $ly1 = $y;\n                    $ly2 = $y;\n                    $lx1 = $x;\n                    $lx2 = $x2;\n                    $this->SetLineWidth($Tw);\n                    if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {\n                        if ($Tw > $Lw) {\n                            $xadj = ($Tw - $Lw) / 2;\n                        }\n                        if ($Tw < $Lw) {\n                            $xadj = ($Tw + $Lw) / 2;\n                        }\n                    } else {\n                        $xadj = $Tw / 2 - $bsh / 2;\n                    }\n                    if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {\n                        if ($Tw > $Rw) {\n                            $xadj2 = ($Tw - $Rw) / 2;\n                        }\n                        if ($Tw < $Rw) {\n                            $xadj2 = ($Tw + $Rw) / 2;\n                        }\n                    } else {\n                        $xadj2 = $Tw / 2 - $bsh / 2;\n                    }\n                    if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TL'])) {\n                        $xadj = ($Tw - $details['mbw']['TL']) / 2;\n                    }\n                    if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['TR'])) {\n                        $xadj2 = ($Tw - $details['mbw']['TR']) / 2;\n                    }\n                    $print = true;\n                }\n                if ($Lw && $side == 'L' && $this->issetBorder($bord, Border::LEFT)) { // LEFT\n                    $ly1 = $y;\n                    $ly2 = $y2;\n                    $lx1 = $x;\n                    $lx2 = $x;\n                    $this->SetLineWidth($Lw);\n                    if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {\n                        if ($Lw > $Tw) {\n                            $yadj = ($Lw - $Tw) / 2;\n                        }\n                        if ($Lw < $Tw) {\n                            $yadj = ($Lw + $Tw) / 2;\n                        }\n                    } else {\n                        $yadj = $Lw / 2 - $bsv / 2;\n                    }\n                    if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {\n                        if ($Lw > $Bw) {\n                            $yadj2 = ($Lw - $Bw) / 2;\n                        }\n                        if ($Lw < $Bw) {\n                            $yadj2 = ($Lw + $Bw) / 2;\n                        }\n                    } else {\n                        $yadj2 = $Lw / 2 - $bsv / 2;\n                    }\n                    if (!$bSeparate && $details['mbw']['LT']) {\n                        $yadj = ($Lw - $details['mbw']['LT']) / 2;\n                    }\n                    if (!$bSeparate && $details['mbw']['LB']) {\n                        $yadj2 = ($Lw - $details['mbw']['LB']) / 2;\n                    }\n                    $print = true;\n                }\n                if ($Rw && $side == 'R' && $this->issetBorder($bord, Border::RIGHT)) { // RIGHT\n                    $ly1 = $y;\n                    $ly2 = $y2;\n                    $lx1 = $x2;\n                    $lx2 = $x2;\n                    $this->SetLineWidth($Rw);\n                    if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {\n                        if ($Rw < $Tw) {\n                            $yadj = ($Rw + $Tw) / 2;\n                        }\n                        if ($Rw > $Tw) {\n                            $yadj = ($Rw - $Tw) / 2;\n                        }\n                    } else {\n                        $yadj = $Rw / 2 - $bsv / 2;\n                    }\n\n                    if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {\n                        if ($Rw > $Bw) {\n                            $yadj2 = ($Rw - $Bw) / 2;\n                        }\n                        if ($Rw < $Bw) {\n                            $yadj2 = ($Rw + $Bw) / 2;\n                        }\n                    } else {\n                        $yadj2 = $Rw / 2 - $bsv / 2;\n                    }\n\n                    if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RT'])) {\n                        $yadj = ($Rw - $details['mbw']['RT']) / 2;\n                    }\n                    if (!$bSeparate && !empty($details['mbw']) && !empty($details['mbw']['RB'])) {\n                        $yadj2 = ($Rw - $details['mbw']['RB']) / 2;\n                    }\n                    $print = true;\n                }\n                if ($Bw && $side == 'B' && $this->issetBorder($bord, Border::BOTTOM)) { // BOTTOM\n                    $ly1 = $y2;\n                    $ly2 = $y2;\n                    $lx1 = $x;\n                    $lx2 = $x2;\n                    $this->SetLineWidth($Bw);\n                    if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {\n                        if ($Bw > $Lw) {\n                            $xadj = ($Bw - $Lw) / 2;\n                        }\n                        if ($Bw < $Lw) {\n                            $xadj = ($Bw + $Lw) / 2;\n                        }\n                    } else {\n                        $xadj = $Bw / 2 - $bsh / 2;\n                    }\n                    if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {\n                        if ($Bw > $Rw) {\n                            $xadj2 = ($Bw - $Rw) / 2;\n                        }\n                        if ($Bw < $Rw) {\n                            $xadj2 = ($Bw + $Rw) / 2;\n                        }\n                    } else {\n                        $xadj2 = $Bw / 2 - $bsh / 2;\n                    }\n                    if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BL'])) {\n                        $xadj = ($Bw - $details['mbw']['BL']) / 2;\n                    }\n                    if (!$bSeparate && isset($details['mbw']) && isset($details['mbw']['BR'])) {\n                        $xadj2 = ($Bw - $details['mbw']['BR']) / 2;\n                    }\n                    $print = true;\n                }\n\n                // Now draw line\n                if ($print) {\n                    /* -- TABLES-ADVANCED-BORDERS -- */\n                    if ($details[$side]['style'] == 'double') {\n                        if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {\n                            if ($details[$side]['c']) {\n                                $this->SetDColor($details[$side]['c']);\n                            } else {\n                                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                            }\n                            $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);\n                        }\n                        if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {\n                            if ($bSeparate && $cort == 'table') {\n                                if ($side == 'T') {\n                                    $xadj -= $this->LineWidth / 2;\n                                    $xadj2 -= $this->LineWidth;\n                                    if ($this->issetBorder($bord, Border::LEFT)) {\n                                        $xadj += $this->LineWidth / 2;\n                                    }\n                                    if ($this->issetBorder($bord, Border::RIGHT)) {\n                                        $xadj2 += $this->LineWidth;\n                                    }\n                                }\n                                if ($side == 'L') {\n                                    $yadj -= $this->LineWidth / 2;\n                                    $yadj2 -= $this->LineWidth;\n                                    if ($this->issetBorder($bord, Border::TOP)) {\n                                        $yadj += $this->LineWidth / 2;\n                                    }\n                                    if ($this->issetBorder($bord, Border::BOTTOM)) {\n                                        $yadj2 += $this->LineWidth;\n                                    }\n                                }\n                                if ($side == 'B') {\n                                    $xadj -= $this->LineWidth / 2;\n                                    $xadj2 -= $this->LineWidth;\n                                    if ($this->issetBorder($bord, Border::LEFT)) {\n                                        $xadj += $this->LineWidth / 2;\n                                    }\n                                    if ($this->issetBorder($bord, Border::RIGHT)) {\n                                        $xadj2 += $this->LineWidth;\n                                    }\n                                }\n                                if ($side == 'R') {\n                                    $yadj -= $this->LineWidth / 2;\n                                    $yadj2 -= $this->LineWidth;\n                                    if ($this->issetBorder($bord, Border::TOP)) {\n                                        $yadj += $this->LineWidth / 2;\n                                    }\n                                    if ($this->issetBorder($bord, Border::BOTTOM)) {\n                                        $yadj2 += $this->LineWidth;\n                                    }\n                                }\n                            }\n\n                            $this->SetLineWidth($this->LineWidth / 3);\n\n                            $tbcol = $this->colorConverter->convert(255, $this->PDFAXwarnings);\n                            for ($l = 0; $l <= $this->blklvl; $l++) {\n                                if ($this->blk[$l]['bgcolor']) {\n                                    $tbcol = ($this->blk[$l]['bgcolorarray']);\n                                }\n                            }\n\n                            if ($bSeparate) {\n                                $cellBorderOverlay[] = [\n                                    'x' => $lx1 + $xadj,\n                                    'y' => $ly1 + $yadj,\n                                    'x2' => $lx2 - $xadj2,\n                                    'y2' => $ly2 - $yadj2,\n                                    'col' => $tbcol,\n                                    'lw' => $this->LineWidth,\n                                ];\n                            } else {\n                                $this->SetDColor($tbcol);\n                                $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);\n                            }\n                        }\n                    } elseif (isset($details[$side]['style']) && ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset')) {\n                        if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {\n                            if ($details[$side]['c']) {\n                                $this->SetDColor($details[$side]['c']);\n                            } else {\n                                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                            }\n                            if ($details[$side]['style'] == 'outset' || $details[$side]['style'] == 'groove') {\n                                $nc = $this->colorConverter->darken($details[$side]['c']);\n                                $this->SetDColor($nc);\n                            } elseif ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {\n                                $nc = $this->colorConverter->lighten($details[$side]['c']);\n                                $this->SetDColor($nc);\n                            }\n                            $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);\n                        }\n                        if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {\n                            if ($details[$side]['c']) {\n                                $this->SetDColor($details[$side]['c']);\n                            } else {\n                                $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                            }\n                            $doubleadj = ($this->LineWidth) / 3;\n                            $this->SetLineWidth($this->LineWidth / 2);\n                            $xadj3 = $yadj3 = $wadj3 = $hadj3 = 0;\n\n                            if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {\n                                $nc = $this->colorConverter->darken($details[$side]['c']);\n\n                                if ($bSeparate && $cort == 'table') {\n                                    if ($side == 'T') {\n                                        $yadj3 = $this->LineWidth / 2;\n                                        $xadj3 = -$this->LineWidth / 2;\n                                        $wadj3 = $this->LineWidth;\n                                        if ($this->issetBorder($bord, Border::LEFT)) {\n                                            $xadj3 += $this->LineWidth;\n                                            $wadj3 -= $this->LineWidth;\n                                        }\n                                        if ($this->issetBorder($bord, Border::RIGHT)) {\n                                            $wadj3 -= $this->LineWidth * 2;\n                                        }\n                                    }\n                                    if ($side == 'L') {\n                                        $xadj3 = $this->LineWidth / 2;\n                                        $yadj3 = -$this->LineWidth / 2;\n                                        $hadj3 = $this->LineWidth;\n                                        if ($this->issetBorder($bord, Border::TOP)) {\n                                            $yadj3 += $this->LineWidth;\n                                            $hadj3 -= $this->LineWidth;\n                                        }\n                                        if ($this->issetBorder($bord, Border::BOTTOM)) {\n                                            $hadj3 -= $this->LineWidth * 2;\n                                        }\n                                    }\n                                    if ($side == 'B') {\n                                        $yadj3 = $this->LineWidth / 2;\n                                        $xadj3 = -$this->LineWidth / 2;\n                                        $wadj3 = $this->LineWidth;\n                                    }\n                                    if ($side == 'R') {\n                                        $xadj3 = $this->LineWidth / 2;\n                                        $yadj3 = -$this->LineWidth / 2;\n                                        $hadj3 = $this->LineWidth;\n                                    }\n                                } elseif ($side == 'T') {\n                                    $yadj3 = $this->LineWidth / 2;\n                                    $xadj3 = $this->LineWidth / 2;\n                                    $wadj3 = -$this->LineWidth * 2;\n                                } elseif ($side == 'L') {\n                                    $xadj3 = $this->LineWidth / 2;\n                                    $yadj3 = $this->LineWidth / 2;\n                                    $hadj3 = -$this->LineWidth * 2;\n                                } elseif ($side == 'B' && $bSeparate) {\n                                    $yadj3 = $this->LineWidth / 2;\n                                    $wadj3 = $this->LineWidth / 2;\n                                } elseif ($side == 'R' && $bSeparate) {\n                                    $xadj3 = $this->LineWidth / 2;\n                                    $hadj3 = $this->LineWidth / 2;\n                                } elseif ($side == 'B') {\n                                    $yadj3 = $this->LineWidth / 2;\n                                    $xadj3 = $this->LineWidth / 2;\n                                } elseif ($side == 'R') {\n                                    $xadj3 = $this->LineWidth / 2;\n                                    $yadj3 = $this->LineWidth / 2;\n                                }\n                            } else {\n                                $nc = $this->colorConverter->lighten($details[$side]['c']);\n\n                                if ($bSeparate && $cort == 'table') {\n                                    if ($side == 'T') {\n                                        $yadj3 = $this->LineWidth / 2;\n                                        $xadj3 = -$this->LineWidth / 2;\n                                        $wadj3 = $this->LineWidth;\n                                        if ($this->issetBorder($bord, Border::LEFT)) {\n                                            $xadj3 += $this->LineWidth;\n                                            $wadj3 -= $this->LineWidth;\n                                        }\n                                    }\n                                    if ($side == 'L') {\n                                        $xadj3 = $this->LineWidth / 2;\n                                        $yadj3 = -$this->LineWidth / 2;\n                                        $hadj3 = $this->LineWidth;\n                                        if ($this->issetBorder($bord, Border::TOP)) {\n                                            $yadj3 += $this->LineWidth;\n                                            $hadj3 -= $this->LineWidth;\n                                        }\n                                    }\n                                    if ($side == 'B') {\n                                        $yadj3 = $this->LineWidth / 2;\n                                        $xadj3 = -$this->LineWidth / 2;\n                                        $wadj3 = $this->LineWidth;\n                                        if ($this->issetBorder($bord, Border::LEFT)) {\n                                            $xadj3 += $this->LineWidth;\n                                            $wadj3 -= $this->LineWidth;\n                                        }\n                                    }\n                                    if ($side == 'R') {\n                                        $xadj3 = $this->LineWidth / 2;\n                                        $yadj3 = -$this->LineWidth / 2;\n                                        $hadj3 = $this->LineWidth;\n                                        if ($this->issetBorder($bord, Border::TOP)) {\n                                            $yadj3 += $this->LineWidth;\n                                            $hadj3 -= $this->LineWidth;\n                                        }\n                                    }\n                                } elseif ($side == 'T') {\n                                    $yadj3 = $this->LineWidth / 2;\n                                    $xadj3 = $this->LineWidth / 2;\n                                } elseif ($side == 'L') {\n                                    $xadj3 = $this->LineWidth / 2;\n                                    $yadj3 = $this->LineWidth / 2;\n                                } elseif ($side == 'B' && $bSeparate) {\n                                    $yadj3 = $this->LineWidth / 2;\n                                    $xadj3 = $this->LineWidth / 2;\n                                } elseif ($side == 'R' && $bSeparate) {\n                                    $xadj3 = $this->LineWidth / 2;\n                                    $yadj3 = $this->LineWidth / 2;\n                                } elseif ($side == 'B') {\n                                    $yadj3 = $this->LineWidth / 2;\n                                    $xadj3 = -$this->LineWidth / 2;\n                                    $wadj3 = $this->LineWidth;\n                                } elseif ($side == 'R') {\n                                    $xadj3 = $this->LineWidth / 2;\n                                    $yadj3 = -$this->LineWidth / 2;\n                                    $hadj3 = $this->LineWidth;\n                                }\n                            }\n\n                            if ($bSeparate) {\n                                $cellBorderOverlay[] = [\n                                    'x' => $lx1 + $xadj + $xadj3,\n                                    'y' => $ly1 + $yadj + $yadj3,\n                                    'x2' => $lx2 - $xadj2 + $xadj3 + $wadj3,\n                                    'y2' => $ly2 - $yadj2 + $yadj3 + $hadj3,\n                                    'col' => $nc,\n                                    'lw' => $this->LineWidth,\n                                ];\n                            } else {\n                                $this->SetDColor($nc);\n                                $this->Line($lx1 + $xadj + $xadj3, $ly1 + $yadj + $yadj3, $lx2 - $xadj2 + $xadj3 + $wadj3, $ly2 - $yadj2 + $yadj3 + $hadj3);\n                            }\n                        }\n                    } else {\n                        /* -- END TABLES-ADVANCED-BORDERS -- */\n                        if ($details[$side]['style'] == 'dashed') {\n                            $dashsize = 2; // final dash will be this + 1*linewidth\n                            $dashsizek = 1.5; // ratio of Dash/Blank\n                            $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));\n                        } elseif ($details[$side]['style'] == 'dotted') {\n                            $this->SetLineJoin(1);\n                            $this->SetLineCap(1);\n                            $this->SetDash(0.001, ($this->LineWidth * 2));\n                        }\n                        if ($details[$side]['c']) {\n                            $this->SetDColor($details[$side]['c']);\n                        } else {\n                            $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n                        }\n                        $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);\n                        /* -- TABLES-ADVANCED-BORDERS -- */\n                    }\n                    /* -- END TABLES-ADVANCED-BORDERS -- */\n\n                    // Reset Corners\n                    $this->SetDash();\n                    // BUTT style line cap\n                    $this->SetLineCap(2);\n                }\n            }\n\n            if ($bSeparate && count($cellBorderOverlay)) {\n                foreach ($cellBorderOverlay as $cbo) {\n                    $this->SetLineWidth($cbo['lw']);\n                    $this->SetDColor($cbo['col']);\n                    $this->Line($cbo['x'], $cbo['y'], $cbo['x2'], $cbo['y2']);\n                }\n            }\n\n            // $this->SetLineWidth($oldlinewidth);\n            // $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        }\n    }\n\n    /* -- TABLES -- */\n    /* -- TABLES-ADVANCED-BORDERS -- */\n\n    /* -- END TABLES-ADVANCED-BORDERS -- */\n\n    function setBorder(&$var, $flag, $set = true)\n    {\n        $flag = intval($flag);\n        if ($set) {\n            $set = true;\n        }\n        $var = intval($var);\n        $var = $set ? ($var | $flag) : ($var & ~$flag);\n    }\n\n    function issetBorder($var, $flag)\n    {\n        $flag = intval($flag);\n        $var = intval($var);\n        return (($var & $flag) == $flag);\n    }\n\n    function _table2cellBorder(&$tableb, &$cbdb, &$cellb, $bval)\n    {\n        if ($tableb && $tableb['w'] > $cbdb['w']) {\n            $cbdb = $tableb;\n            $this->setBorder($cellb, $bval);\n        } elseif ($tableb && $tableb['w'] == $cbdb['w'] && array_search($tableb['style'], $this->borderstyles) > array_search($cbdb['style'], $this->borderstyles)) {\n            $cbdb = $tableb;\n            $this->setBorder($cellb, $bval);\n        }\n    }\n\n    // FIX BORDERS ********************************************\n    function _fixTableBorders(&$table)\n    {\n        if (!$table['borders_separate'] && $table['border_details']['L']['w']) {\n            $table['max_cell_border_width']['L'] = $table['border_details']['L']['w'];\n        }\n        if (!$table['borders_separate'] && $table['border_details']['R']['w']) {\n            $table['max_cell_border_width']['R'] = $table['border_details']['R']['w'];\n        }\n        if (!$table['borders_separate'] && $table['border_details']['T']['w']) {\n            $table['max_cell_border_width']['T'] = $table['border_details']['T']['w'];\n        }\n        if (!$table['borders_separate'] && $table['border_details']['B']['w']) {\n            $table['max_cell_border_width']['B'] = $table['border_details']['B']['w'];\n        }\n        if ($this->simpleTables) {\n            return;\n        }\n        $cells = &$table['cells'];\n        $numcols = $table['nc'];\n        $numrows = $table['nr'];\n        /* -- TABLES-ADVANCED-BORDERS -- */\n        if (isset($table['topntail']) && $table['topntail']) {\n            $tntborddet = $this->border_details($table['topntail']);\n        }\n        if (isset($table['thead-underline']) && $table['thead-underline']) {\n            $thuborddet = $this->border_details($table['thead-underline']);\n        }\n        /* -- END TABLES-ADVANCED-BORDERS -- */\n\n        for ($i = 0; $i < $numrows; $i++) { // Rows\n            for ($j = 0; $j < $numcols; $j++) { // Columns\n                if (isset($cells[$i][$j]) && $cells[$i][$j]) {\n                    $cell = &$cells[$i][$j];\n                    if ($this->packTableData) {\n                        $cbord = $this->_unpackCellBorder($cell['borderbin']);\n                    } else {\n                        $cbord = &$cells[$i][$j];\n                    }\n\n                    // mPDF 5.7.3\n                    if (!$cbord['border'] && $cbord['border'] !== 0 && isset($table['border']) && $table['border'] && $this->table_border_attr_set) {\n                        $cbord['border'] = $table['border'];\n                        $cbord['border_details'] = $table['border_details'];\n                    }\n\n                    if (isset($cell['colspan']) && $cell['colspan'] > 1) {\n                        $ccolsp = $cell['colspan'];\n                    } else {\n                        $ccolsp = 1;\n                    }\n                    if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {\n                        $crowsp = $cell['rowspan'];\n                    } else {\n                        $crowsp = 1;\n                    }\n\n                    $cbord['border_details']['cellposdom'] = ((($i + 1) / $numrows) / 10000 ) + ((($j + 1) / $numcols) / 10 );\n                    // Inherit Cell border from Table border\n                    if ($this->table_border_css_set && !$table['borders_separate']) {\n                        if ($i == 0) {\n                            $this->_table2cellBorder($table['border_details']['T'], $cbord['border_details']['T'], $cbord['border'], Border::TOP);\n                        }\n                        if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {\n                            $this->_table2cellBorder($table['border_details']['B'], $cbord['border_details']['B'], $cbord['border'], Border::BOTTOM);\n                        }\n                        if ($j == 0) {\n                            $this->_table2cellBorder($table['border_details']['L'], $cbord['border_details']['L'], $cbord['border'], Border::LEFT);\n                        }\n                        if ($j == ($numcols - 1) || ($j + $ccolsp) == ($numcols)) {\n                            $this->_table2cellBorder($table['border_details']['R'], $cbord['border_details']['R'], $cbord['border'], Border::RIGHT);\n                        }\n                    }\n\n                    /* -- TABLES-ADVANCED-BORDERS -- */\n                    $fixbottom = true;\n                    if (isset($table['topntail']) && $table['topntail']) {\n                        if ($i == 0) {\n                            $cbord['border_details']['T'] = $tntborddet;\n                            $this->setBorder($cbord['border'], Border::TOP);\n                        }\n                        if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {\n                            $cbord['border_details']['B'] = $tntborddet;\n                            $this->setBorder($cbord['border'], Border::BOTTOM);\n                            $fixbottom = false;\n                        } elseif ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows']) {\n                            if (!$table['borders_separate']) {\n                                $cbord['border_details']['T'] = $tntborddet;\n                                $this->setBorder($cbord['border'], Border::TOP);\n                            }\n                        }\n                        if ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'] - 1)) {\n                            if (!$table['borders_separate']) {\n                                $cbord['border_details']['B'] = $tntborddet;\n                                $this->setBorder($cbord['border'], Border::BOTTOM);\n                                $fixbottom = false;\n                            }\n                        } elseif ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'])) {\n                            $cbord['border_details']['T'] = $tntborddet;\n                            $this->setBorder($cbord['border'], Border::TOP);\n                        }\n                        if ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader\n                            if (!$table['borders_separate']) {\n                                $cbord['border_details']['T'] = $tntborddet;\n                                $this->setBorder($cbord['border'], Border::TOP);\n                            }\n                        }\n                        if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {\n                            $cbord['border_details']['B'] = $tntborddet;\n                            $this->setBorder($cbord['border'], Border::BOTTOM);\n                        }\n                    }\n                    if (isset($table['thead-underline']) && $table['thead-underline']) {\n                        if ($table['borders_separate']) {\n                            if ($i == 0) {\n                                $cbord['border_details']['B'] = $thuborddet;\n                                $this->setBorder($cbord['border'], Border::BOTTOM);\n                                $fixbottom = false;\n                            }\n                        } else {\n                            if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {\n                                $cbord['border_details']['T'] = $thuborddet;\n                                $this->setBorder($cbord['border'], Border::TOP);\n                            } elseif ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader\n                                $cbord['border_details']['T'] = $thuborddet;\n                                $this->setBorder($cbord['border'], Border::TOP);\n                            }\n                        }\n                    }\n\n                    // Collapse Border - Algorithm for conflicting borders\n                    // Hidden >> Width >> double>solid>dashed>dotted... >> style set on cell>table >> top/left>bottom/right\n                    // Do not turn off border which is overridden\n                    // Needed for page break for TOP/BOTTOM both to be defined in Collapsed borders\n                    // Means it is painted twice. (Left/Right can still disable overridden border)\n                    if (!$table['borders_separate']) {\n\n                        if (($i < ($numrows - 1) || ($i + $crowsp) < $numrows ) && $fixbottom) { // Bottom\n\n                            for ($cspi = 0; $cspi < $ccolsp; $cspi++) {\n\n                                // already defined Top for adjacent cell below\n                                if (isset($cells[($i + $crowsp)][$j + $cspi])) {\n                                    if ($this->packTableData) {\n                                        $adjc = $cells[($i + $crowsp)][$j + $cspi];\n                                        $celladj = $this->_unpackCellBorder($adjc['borderbin']);\n                                    } else {\n                                        $celladj = & $cells[($i + $crowsp)][$j + $cspi];\n                                    }\n                                } else {\n                                    $celladj = false;\n                                }\n\n                                if (isset($celladj['border_details']['T']['s']) && $celladj['border_details']['T']['s'] == 1) {\n\n                                    $csadj = $celladj['border_details']['T']['w'];\n                                    $csthis = $cbord['border_details']['B']['w'];\n\n                                    // Hidden\n                                    if ($cbord['border_details']['B']['style'] == 'hidden') {\n\n                                        $celladj['border_details']['T'] = $cbord['border_details']['B'];\n                                        $this->setBorder($celladj['border'], Border::TOP, false);\n                                        $this->setBorder($cbord['border'], Border::BOTTOM, false);\n\n                                    } elseif ($celladj['border_details']['T']['style'] == 'hidden') {\n\n                                        $cbord['border_details']['B'] = $celladj['border_details']['T'];\n                                        $this->setBorder($cbord['border'], Border::BOTTOM, false);\n                                        $this->setBorder($celladj['border'], Border::TOP, false);\n\n                                    } elseif ($csthis > $csadj) { // Width\n\n                                        if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span\n                                            $celladj['border_details']['T'] = $cbord['border_details']['B'];\n                                            $this->setBorder($cbord['border'], Border::BOTTOM);\n                                        }\n\n                                    } elseif ($csadj > $csthis) {\n\n                                        if ($ccolsp < 2) { // don't overwrite this cell if it spans\n                                            $cbord['border_details']['B'] = $celladj['border_details']['T'];\n                                            $this->setBorder($celladj['border'], Border::TOP);\n                                        }\n\n                                    } elseif (array_search($cbord['border_details']['B']['style'], $this->borderstyles) > array_search($celladj['border_details']['T']['style'], $this->borderstyles)) { // double>solid>dashed>dotted...\n\n                                        if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span\n                                            $celladj['border_details']['T'] = $cbord['border_details']['B'];\n                                            $this->setBorder($cbord['border'], Border::BOTTOM);\n                                        }\n\n                                    } elseif (array_search($celladj['border_details']['T']['style'], $this->borderstyles) > array_search($cbord['border_details']['B']['style'], $this->borderstyles)) {\n\n                                        if ($ccolsp < 2) { // don't overwrite this cell if it spans\n                                            $cbord['border_details']['B'] = $celladj['border_details']['T'];\n                                            $this->setBorder($celladj['border'], Border::TOP);\n                                        }\n\n                                    } elseif ($celladj['border_details']['T']['dom'] > $celladj['border_details']['B']['dom']) { // Style set on cell vs. table\n\n                                        if ($ccolsp < 2) { // don't overwrite this cell if it spans\n                                            $cbord['border_details']['B'] = $celladj['border_details']['T'];\n                                            $this->setBorder($celladj['border'], Border::TOP);\n                                        }\n\n                                    } else { // Style set on cell vs. table  - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT\n\n                                        if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span\n                                            $celladj['border_details']['T'] = $cbord['border_details']['B'];\n                                            $this->setBorder($cbord['border'], Border::BOTTOM);\n                                        }\n\n                                    }\n\n                                } elseif ($celladj) {\n\n                                    if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span\n                                        $celladj['border_details']['T'] = $cbord['border_details']['B'];\n                                    }\n\n                                }\n\n                                // mPDF 5.7.4\n                                if ($celladj && $this->packTableData) {\n                                    $cells[$i + $crowsp][$j + $cspi]['borderbin'] = $this->_packCellBorder($celladj);\n                                }\n\n                                unset($celladj);\n                            }\n                        }\n\n                        if ($j < ($numcols - 1) || ($j + $ccolsp) < $numcols) { // Right-Left\n\n                            for ($cspi = 0; $cspi < $crowsp; $cspi++) {\n\n                                // already defined Left for adjacent cell to R\n                                if (isset($cells[($i + $cspi)][$j + $ccolsp])) {\n                                    if ($this->packTableData) {\n                                        $adjc = $cells[($i + $cspi)][$j + $ccolsp];\n                                        $celladj = $this->_unpackCellBorder($adjc['borderbin']);\n                                    } else {\n                                        $celladj = & $cells[$i + $cspi][$j + $ccolsp];\n                                    }\n                                } else {\n                                    $celladj = false;\n                                }\n                                if ($celladj && $celladj['border_details']['L']['s'] == 1) {\n                                    $csadj = $celladj['border_details']['L']['w'];\n                                    $csthis = $cbord['border_details']['R']['w'];\n                                    // Hidden\n                                    if ($cbord['border_details']['R']['style'] == 'hidden') {\n                                        $celladj['border_details']['L'] = $cbord['border_details']['R'];\n                                        $this->setBorder($celladj['border'], Border::LEFT, false);\n                                        $this->setBorder($cbord['border'], Border::RIGHT, false);\n                                    } elseif ($celladj['border_details']['L']['style'] == 'hidden') {\n                                        $cbord['border_details']['R'] = $celladj['border_details']['L'];\n                                        $this->setBorder($cbord['border'], Border::RIGHT, false);\n                                        $this->setBorder($celladj['border'], Border::LEFT, false);\n                                    } // Width\n                                    elseif ($csthis > $csadj) {\n                                        if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span\n                                            $celladj['border_details']['L'] = $cbord['border_details']['R'];\n                                            $this->setBorder($cbord['border'], Border::RIGHT);\n                                            $this->setBorder($celladj['border'], Border::LEFT, false);\n                                        }\n                                    } elseif ($csadj > $csthis) {\n                                        if ($crowsp < 2) { // don't overwrite this cell if it spans\n                                            $cbord['border_details']['R'] = $celladj['border_details']['L'];\n                                            $this->setBorder($cbord['border'], Border::RIGHT, false);\n                                            $this->setBorder($celladj['border'], Border::LEFT);\n                                        }\n                                    } // double>solid>dashed>dotted...\n                                    elseif (array_search($cbord['border_details']['R']['style'], $this->borderstyles) > array_search($celladj['border_details']['L']['style'], $this->borderstyles)) {\n                                        if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span\n                                            $celladj['border_details']['L'] = $cbord['border_details']['R'];\n                                            $this->setBorder($celladj['border'], Border::LEFT, false);\n                                            $this->setBorder($cbord['border'], Border::RIGHT);\n                                        }\n                                    } elseif (array_search($celladj['border_details']['L']['style'], $this->borderstyles) > array_search($cbord['border_details']['R']['style'], $this->borderstyles)) {\n                                        if ($crowsp < 2) { // don't overwrite this cell if it spans\n                                            $cbord['border_details']['R'] = $celladj['border_details']['L'];\n                                            $this->setBorder($cbord['border'], Border::RIGHT, false);\n                                            $this->setBorder($celladj['border'], Border::LEFT);\n                                        }\n                                    } // Style set on cell vs. table\n                                    elseif ($celladj['border_details']['L']['dom'] > $cbord['border_details']['R']['dom']) {\n                                        if ($crowsp < 2) { // don't overwrite this cell if it spans\n                                            $cbord['border_details']['R'] = $celladj['border_details']['L'];\n                                            $this->setBorder($celladj['border'], Border::LEFT);\n                                        }\n                                    } // Style set on cell vs. table  - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT\n                                    else {\n                                        if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span\n                                            $celladj['border_details']['L'] = $cbord['border_details']['R'];\n                                            $this->setBorder($cbord['border'], Border::RIGHT);\n                                        }\n                                    }\n                                } elseif ($celladj) {\n                                    // if right-cell border is not set\n                                    if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span\n                                        $celladj['border_details']['L'] = $cbord['border_details']['R'];\n                                    }\n                                }\n                                // mPDF 5.7.4\n                                if ($celladj && $this->packTableData) {\n                                    $cells[$i + $cspi][$j + $ccolsp]['borderbin'] = $this->_packCellBorder($celladj);\n                                }\n                                unset($celladj);\n                            }\n                        }\n                    }\n\n\n                    // Set maximum cell border width meeting at LRTB edges of cell - used for extended cell border\n                    // ['border_details']['mbw']['LT'] = meeting border width - Left border - Top end\n                    if (!$table['borders_separate']) {\n\n                        $cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $cbord['border_details']['L']['w']);\n                        $cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $cbord['border_details']['R']['w']);\n                        $cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $cbord['border_details']['T']['w']);\n                        $cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $cbord['border_details']['B']['w']);\n                        $cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $cbord['border_details']['L']['w']);\n                        $cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $cbord['border_details']['R']['w']);\n                        $cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $cbord['border_details']['T']['w']);\n                        $cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $cbord['border_details']['B']['w']);\n\n                        if (($i + $crowsp) < $numrows && isset($cells[$i + $crowsp][$j])) { // Has Bottom adjoining cell\n\n                            if ($this->packTableData) {\n                                $adjc = $cells[$i + $crowsp][$j];\n                                $celladj = $this->_unpackCellBorder($adjc['borderbin']);\n                            } else {\n                                $celladj = & $cells[$i + $crowsp][$j];\n                            }\n\n                            $cbord['border_details']['mbw']['BL'] = max(\n                                $cbord['border_details']['mbw']['BL'],\n                                $celladj ? $celladj['border_details']['L']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['TL']: 0\n                            );\n\n                            $cbord['border_details']['mbw']['BR'] = max(\n                                $cbord['border_details']['mbw']['BR'],\n                                $celladj ? $celladj['border_details']['R']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['TR']: 0\n                            );\n\n                            $cbord['border_details']['mbw']['LB'] = max(\n                                $cbord['border_details']['mbw']['LB'],\n                                $celladj ? $celladj['border_details']['mbw']['LT'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['RB'] = max(\n                                $cbord['border_details']['mbw']['RB'],\n                                $celladj ? $celladj['border_details']['mbw']['RT'] : 0\n                            );\n\n                            unset($celladj);\n                        }\n\n                        if (($j + $ccolsp) < $numcols && isset($cells[$i][$j + $ccolsp])) { // Has Right adjoining cell\n\n                            if ($this->packTableData) {\n                                $adjc = $cells[$i][$j + $ccolsp];\n                                $celladj = $this->_unpackCellBorder($adjc['borderbin']);\n                            } else {\n                                $celladj = & $cells[$i][$j + $ccolsp];\n                            }\n\n                            $cbord['border_details']['mbw']['RT'] = max(\n                                $cbord['border_details']['mbw']['RT'],\n                                $celladj ? $celladj['border_details']['T']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['LT'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['RB'] = max(\n                                $cbord['border_details']['mbw']['RB'],\n                                $celladj ? $celladj['border_details']['B']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['LB'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['TR'] = max(\n                                $cbord['border_details']['mbw']['TR'],\n                                $celladj ? $celladj['border_details']['mbw']['TL'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['BR'] = max(\n                                $cbord['border_details']['mbw']['BR'],\n                                $celladj ? $celladj['border_details']['mbw']['BL'] : 0\n                            );\n\n                            unset($celladj);\n                        }\n\n                        if ($i > 0 && isset($cells[$i - 1][$j]) && is_array($cells[$i - 1][$j]) && (($this->packTableData && $cells[$i - 1][$j]['borderbin']) || $cells[$i - 1][$j]['border'])) { // Has Top adjoining cell\n\n                            if ($this->packTableData) {\n                                $adjc = $cells[$i - 1][$j];\n                                $celladj = $this->_unpackCellBorder($adjc['borderbin']);\n                            } else {\n                                $celladj = & $cells[$i - 1][$j];\n                            }\n\n                            $cbord['border_details']['mbw']['TL'] = max(\n                                $cbord['border_details']['mbw']['TL'],\n                                $celladj ? $celladj['border_details']['L']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['BL'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['TR'] = max(\n                                $cbord['border_details']['mbw']['TR'],\n                                $celladj ? $celladj['border_details']['R']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['BR'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['LT'] = max(\n                                $cbord['border_details']['mbw']['LT'],\n                                $celladj ? $celladj['border_details']['mbw']['LB'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['RT'] = max(\n                                $cbord['border_details']['mbw']['RT'],\n                                $celladj ? $celladj['border_details']['mbw']['RB'] : 0\n                            );\n\n                            if ($celladj['border_details']['mbw']['BL']) {\n                                $celladj['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['BL']);\n                            }\n\n                            if ($celladj['border_details']['mbw']['BR']) {\n                                $celladj['border_details']['mbw']['BR'] = max($celladj['border_details']['mbw']['BR'], $cbord['border_details']['mbw']['TR']);\n                            }\n\n                            if ($this->packTableData) {\n                                $cells[$i - 1][$j]['borderbin'] = $this->_packCellBorder($celladj);\n                            }\n                            unset($celladj);\n                        }\n\n                        if ($j > 0 && isset($cells[$i][$j - 1]) && is_array($cells[$i][$j - 1]) && (($this->packTableData && $cells[$i][$j - 1]['borderbin']) || $cells[$i][$j - 1]['border'])) { // Has Left adjoining cell\n\n                            if ($this->packTableData) {\n                                $adjc = $cells[$i][$j - 1];\n                                $celladj = $this->_unpackCellBorder($adjc['borderbin']);\n                            } else {\n                                $celladj = & $cells[$i][$j - 1];\n                            }\n\n                            $cbord['border_details']['mbw']['LT'] = max(\n                                $cbord['border_details']['mbw']['LT'],\n                                $celladj ? $celladj['border_details']['T']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['RT'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['LB'] = max(\n                                $cbord['border_details']['mbw']['LB'],\n                                $celladj ? $celladj['border_details']['B']['w'] : 0,\n                                $celladj ? $celladj['border_details']['mbw']['RB'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['BL'] = max(\n                                $cbord['border_details']['mbw']['BL'],\n                                $celladj ? $celladj['border_details']['mbw']['BR'] : 0\n                            );\n\n                            $cbord['border_details']['mbw']['TL'] = max(\n                                $cbord['border_details']['mbw']['TL'],\n                                $celladj ? $celladj['border_details']['mbw']['TR'] : 0\n                            );\n\n                            if ($celladj['border_details']['mbw']['RT']) {\n                                $celladj['border_details']['mbw']['RT'] = max($celladj['border_details']['mbw']['RT'], $cbord['border_details']['mbw']['LT']);\n                            }\n\n                            if ($celladj['border_details']['mbw']['RB']) {\n                                $celladj['border_details']['mbw']['RB'] = max($celladj['border_details']['mbw']['RB'], $cbord['border_details']['mbw']['LB']);\n                            }\n\n                            if ($this->packTableData) {\n                                $cells[$i][$j - 1]['borderbin'] = $this->_packCellBorder($celladj);\n                            }\n\n                            unset($celladj);\n                        }\n\n\n                        // Update maximum cell border width at LRTB edges of table - used for overall table width\n                        if ($j == 0 && $cbord['border_details']['L']['w']) {\n                            $table['max_cell_border_width']['L'] = max($table['max_cell_border_width']['L'], $cbord['border_details']['L']['w']);\n                        }\n                        if (($j == ($numcols - 1) || ($j + $ccolsp) == $numcols ) && $cbord['border_details']['R']['w']) {\n                            $table['max_cell_border_width']['R'] = max($table['max_cell_border_width']['R'], $cbord['border_details']['R']['w']);\n                        }\n                        if ($i == 0 && $cbord['border_details']['T']['w']) {\n                            $table['max_cell_border_width']['T'] = max($table['max_cell_border_width']['T'], $cbord['border_details']['T']['w']);\n                        }\n                        if (($i == ($numrows - 1) || ($i + $crowsp) == $numrows ) && $cbord['border_details']['B']['w']) {\n                            $table['max_cell_border_width']['B'] = max($table['max_cell_border_width']['B'], $cbord['border_details']['B']['w']);\n                        }\n                    }\n                    /* -- END TABLES-ADVANCED-BORDERS -- */\n\n                    if ($this->packTableData) {\n                        $cell['borderbin'] = $this->_packCellBorder($cbord);\n                    }\n\n                    unset($cbord);\n\n                    unset($cell);\n                }\n            }\n        }\n        unset($cell);\n    }\n\n    // END FIX BORDERS ************************************************************************************\n\n    function _reverseTableDir(&$table)\n    {\n        $cells = &$table['cells'];\n        $numcols = $table['nc'];\n        $numrows = $table['nr'];\n        for ($i = 0; $i < $numrows; $i++) { // Rows\n            $row = [];\n            for ($j = ($numcols - 1); $j >= 0; $j--) { // Columns\n                if (isset($cells[$i][$j]) && $cells[$i][$j]) {\n                    $cell = &$cells[$i][$j];\n                    $col = $numcols - $j - 1;\n                    if (isset($cell['colspan']) && $cell['colspan'] > 1) {\n                        $col -= ($cell['colspan'] - 1);\n                    }\n                    // Nested content\n                    if (isset($cell['textbuffer'])) {\n                        for ($n = 0; $n < count($cell['textbuffer']); $n++) {\n                            $t = $cell['textbuffer'][$n][0];\n                            if (substr($t, 0, 19) == \"\\xbb\\xa4\\xactype=nestedtable\") {\n                                $objattr = $this->_getObjAttr($t);\n                                $objattr['col'] = $col;\n                                $cell['textbuffer'][$n][0] = \"\\xbb\\xa4\\xactype=nestedtable,objattr=\" . serialize($objattr) . \"\\xbb\\xa4\\xac\";\n                                $this->table[($this->tableLevel + 1)][$objattr['nestedcontent']]['nestedpos'][1] = $col;\n                            }\n                        }\n                    }\n                    $row[$col] = $cells[$i][$j];\n                    unset($cell);\n                }\n            }\n            for ($f = 0; $f < $numcols; $f++) {\n                if (!isset($row[$f])) {\n                    $row[$f] = 0;\n                }\n            }\n            $table['cells'][$i] = $row;\n        }\n    }\n\n    function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $splitpg = 0, $rety = 0)\n    {\n        $level = $table['level'];\n        $levelid = $table['levelid'];\n\n        $cells = &$table['cells'];\n        $numcols = $table['nc'];\n        $numrows = $table['nr'];\n        $maxbwtop = 0;\n        if ($this->ColActive && $level == 1) {\n            $this->breakpoints[$this->CurrCol][] = $this->y;\n        } // *COLUMNS*\n\n        if (!$split || ($startrow == 0 && $splitpg == 0) || $startrow > 0) {\n            // TABLE TOP MARGIN\n            if ($table['margin']['T']) {\n                if (!$this->table_rotate && $level == 1) {\n                    $this->DivLn($table['margin']['T'], $this->blklvl, true, 1);  // collapsible\n                } else {\n                    $this->y += ($table['margin']['T']);\n                }\n            }\n            // Advance down page by half width of top border\n            if ($table['borders_separate']) {\n                if ($startrow > 0 && (!isset($table['is_thead']) || count($table['is_thead']) == 0)) {\n                    $adv = $table['border_spacing_V'] / 2;\n                } else {\n                    $adv = $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;\n                }\n            } else {\n                $adv = $table['max_cell_border_width']['T'] / 2;\n            }\n            if (!$this->table_rotate && $level == 1) {\n                $this->DivLn($adv);\n            } else {\n                $this->y += $adv;\n            }\n        }\n\n        if ($level == 1) {\n            $this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];\n            $x0 = $this->x;\n            $y0 = $this->y;\n            $right = $x0 + $this->blk[$this->blklvl]['inner_width'];\n            $outerfilled = $this->y; // Keep track of how far down the outer DIV bgcolor is painted (NB rowspans)\n            $this->outerfilled = $this->y;\n            $this->colsums = [];\n        } else {\n            $x0 = $this->x;\n            $y0 = $this->y;\n            $right = $x0 + $table['w'];\n        }\n\n        if ($this->table_rotate) {\n            $temppgwidth = $this->tbrot_maxw;\n            $this->PageBreakTrigger = $pagetrigger = $y0 + ($this->blk[$this->blklvl]['inner_width']);\n            if ($level == 1) {\n                $this->tbrot_y0 = $this->y - $adv - $table['margin']['T'];\n                $this->tbrot_x0 = $this->x;\n                $this->tbrot_w = $table['w'];\n                if ($table['borders_separate']) {\n                    $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;\n                } else {\n                    $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['max_cell_border_width']['T'];\n                }\n            }\n        } else {\n            $this->PageBreakTrigger = $pagetrigger = ($this->h - $this->bMargin);\n            if ($level == 1) {\n                $temppgwidth = $this->blk[$this->blklvl]['inner_width'];\n                if (isset($table['a']) and ( $table['w'] < $this->blk[$this->blklvl]['inner_width'])) {\n                    if ($table['a'] == 'C') {\n                        $x0 += ((($right - $x0) - $table['w']) / 2);\n                    } elseif ($table['a'] == 'R') {\n                        $x0 = $right - $table['w'];\n                    }\n                }\n            } else {\n                $temppgwidth = $table['w'];\n            }\n        }\n        if (!isset($table['overflow'])) {\n            $table['overflow'] = null;\n        }\n        if ($table['overflow'] == 'hidden' && $level == 1 && !$this->table_rotate && !$this->ColActive) {\n            // Bounding rectangle to clip\n            $this->tableClipPath = sprintf('q %.3F %.3F %.3F %.3F re W n', $x0 * Mpdf::SCALE, $this->h * Mpdf::SCALE, $this->blk[$this->blklvl]['inner_width'] * Mpdf::SCALE, -$this->h * Mpdf::SCALE);\n            $this->writer->write($this->tableClipPath);\n        } else {\n            $this->tableClipPath = '';\n        }\n\n\n        if ($table['borders_separate']) {\n            $indent = $table['margin']['L'] + $table['border_details']['L']['w'] + $table['padding']['L'] + $table['border_spacing_H'] / 2;\n        } else {\n            $indent = $table['margin']['L'] + $table['max_cell_border_width']['L'] / 2;\n        }\n        $x0 += $indent;\n\n        $returny = 0;\n        $lastCol = 0;\n        $tableheader = [];\n        $tablefooter = [];\n        $tableheaderrowheight = 0;\n        $tablefooterrowheight = 0;\n        $footery = 0;\n\n        // mPD 3.0 Set the Page & Column where table starts\n        if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN\n            $tablestartpage = 'EVEN';\n        } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD\n            $tablestartpage = 'ODD';\n        } else {\n            $tablestartpage = '';\n        }\n        if ($this->ColActive) {\n            $tablestartcolumn = $this->CurrCol;\n        } else {\n            $tablestartcolumn = '';\n        }\n\n        $y = $h = 0;\n        for ($i = 0; $i < $numrows; $i++) { // Rows\n            if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i] && $level == 1) {\n                $tablefooterrowheight += $table['hr'][$i];\n                $tablefooter[$i][0]['trbackground-images'] = $table['trbackground-images'][$i];\n                $tablefooter[$i][0]['trgradients'] = $table['trgradients'][$i];\n                $tablefooter[$i][0]['trbgcolor'] = $table['bgcolor'][$i];\n                for ($j = $startcol; $j < $numcols; $j++) { // Columns\n                    if (isset($cells[$i][$j]) && $cells[$i][$j]) {\n                        $cell = &$cells[$i][$j];\n                        if ($split) {\n                            if ($table['colPg'][$j] != $splitpg) {\n                                continue;\n                            }\n                            [$x, $w] = $this->_splitTableGetWidth($table, $i, $j);\n                            $js = $j - $startcol;\n                        } else {\n                            [$x, $w] = $this->_tableGetWidth($table, $i, $j);\n                            $js = $j;\n                        }\n\n                        [$y, $h] = $this->_tableGetHeight($table, $i, $j);\n                        $x += $x0;\n                        $y += $y0;\n                        // Get info of tfoot ==>> table footer\n                        $tablefooter[$i][$js]['x'] = $x;\n                        $tablefooter[$i][$js]['y'] = $y;\n                        $tablefooter[$i][$js]['h'] = $h;\n                        $tablefooter[$i][$js]['w'] = $w;\n                        if (isset($cell['textbuffer'])) {\n                            $tablefooter[$i][$js]['textbuffer'] = $cell['textbuffer'];\n                        } else {\n                            $tablefooter[$i][$js]['textbuffer'] = '';\n                        }\n                        $tablefooter[$i][$js]['a'] = $cell['a'];\n                        $tablefooter[$i][$js]['R'] = $cell['R'];\n                        $tablefooter[$i][$js]['va'] = $cell['va'];\n                        $tablefooter[$i][$js]['mih'] = $cell['mih'];\n                        if (isset($cell['gradient'])) {\n                            $tablefooter[$i][$js]['gradient'] = $cell['gradient']; // *BACKGROUNDS*\n                        }\n                        if (isset($cell['background-image'])) {\n                            $tablefooter[$i][$js]['background-image'] = $cell['background-image']; // *BACKGROUNDS*\n                        }\n\n                        // CELL FILL BGCOLOR\n                        if (!$this->simpleTables) {\n                            if ($this->packTableData) {\n                                $c = $this->_unpackCellBorder($cell['borderbin']);\n                                $tablefooter[$i][$js]['border'] = $c['border'];\n                                $tablefooter[$i][$js]['border_details'] = $c['border_details'];\n                            } else {\n                                $tablefooter[$i][$js]['border'] = $cell['border'];\n                                $tablefooter[$i][$js]['border_details'] = $cell['border_details'];\n                            }\n                        } elseif ($this->simpleTables) {\n                            $tablefooter[$i][$js]['border'] = $table['simple']['border'];\n                            $tablefooter[$i][$js]['border_details'] = $table['simple']['border_details'];\n                        }\n                        $tablefooter[$i][$js]['bgcolor'] = $cell['bgcolor'];\n                        $tablefooter[$i][$js]['padding'] = $cell['padding'];\n                        if (isset($cell['rowspan'])) {\n                            $tablefooter[$i][$js]['rowspan'] = $cell['rowspan'];\n                        }\n                        if (isset($cell['colspan'])) {\n                            $tablefooter[$i][$js]['colspan'] = $cell['colspan'];\n                        }\n                        if (isset($cell['direction'])) {\n                            $tablefooter[$i][$js]['direction'] = $cell['direction'];\n                        }\n                        if (isset($cell['cellLineHeight'])) {\n                            $tablefooter[$i][$js]['cellLineHeight'] = $cell['cellLineHeight'];\n                        }\n                        if (isset($cell['cellLineStackingStrategy'])) {\n                            $tablefooter[$i][$js]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];\n                        }\n                        if (isset($cell['cellLineStackingShift'])) {\n                            $tablefooter[$i][$js]['cellLineStackingShift'] = $cell['cellLineStackingShift'];\n                        }\n                    }\n                }\n            }\n        }\n\n        if ($level == 1) {\n            $this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr);\n        }\n        $tableheaderadj = 0;\n        $tablefooteradj = 0;\n\n        $tablestartpageno = $this->page;\n\n        // Draw Table Contents and Borders\n        for ($i = 0; $i < $numrows; $i++) { // Rows\n            if ($split && $startrow > 0) {\n                $thnr = (isset($table['is_thead']) ? count($table['is_thead']) : 0);\n                if ($i >= $thnr && $i < $startrow) {\n                    continue;\n                }\n                if ($i == $startrow) {\n                    $returny = $rety - $tableheaderrowheight;\n                }\n            }\n\n            // Get Maximum row/cell height in row - including rowspan>1 + 1 overlapping\n            $maxrowheight = $this->_tableGetMaxRowHeight($table, $i);\n\n            $skippage = false;\n            $newpagestarted = false;\n            for ($j = $startcol; $j < $numcols; $j++) { // Columns\n                if ($split) {\n                    if ($table['colPg'][$j] > $splitpg) {\n                        break;\n                    }\n                    $lastCol = $j;\n                }\n                if (isset($cells[$i][$j]) && $cells[$i][$j]) {\n                    $cell = &$cells[$i][$j];\n                    if ($split) {\n                        $lastCol = $j + (isset($cell['colspan']) ? ($cell['colspan'] - 1) : 0);\n                        [$x, $w] = $this->_splitTableGetWidth($table, $i, $j);\n                    } else {\n                        [$x, $w] = $this->_tableGetWidth($table, $i, $j);\n                    }\n\n                    [$y, $h] = $this->_tableGetHeight($table, $i, $j);\n                    $x += $x0;\n                    $y += $y0;\n                    $y -= $returny;\n\n                    if ($table['borders_separate']) {\n                        if (!empty($tablefooter) || $i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) {\n                            $extra = $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;\n                            // $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V']/2;\n                        } else {\n                            $extra = $table['border_spacing_V'] / 2;\n                        }\n                    } else {\n                        $extra = $table['max_cell_border_width']['B'] / 2;\n                    }\n\n                    if ($j == $startcol && ((($y + $maxrowheight + $extra ) > ($pagetrigger + 0.001)) || (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && ($y + $maxrowheight + $tablefooterrowheight + $extra) > $pagetrigger) && ($this->tableLevel == 1 && $i < ($numrows - $table['headernrows']))) && ($y0 > 0 || $x0 > 0) && !$this->InFooter && $this->autoPageBreak) {\n                        if (!$skippage) {\n                            $finalSpread = true;\n                            $firstSpread = true;\n                            if ($split) {\n                                for ($t = $startcol; $t < $numcols; $t++) {\n                                    // Are there more columns to print on a next page?\n                                    if ($table['colPg'][$t] > $splitpg) {\n                                        $finalSpread = false;\n                                        break;\n                                    }\n                                }\n                                if ($startcol > 0) {\n                                    $firstSpread = false;\n                                }\n                            }\n\n                            if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {\n                                $this->y = $y;\n                                $ya = $this->y;\n                                $this->TableHeaderFooter($tablefooter, $tablestartpage, $tablestartcolumn, 'F', $level, $firstSpread, $finalSpread);\n                                if ($this->table_rotate) {\n                                    $this->tbrot_h += $this->y - $ya;\n                                }\n                                $tablefooteradj = $this->y - $ya;\n                            }\n                            $y -= $y0;\n                            $returny += $y;\n\n                            $oldcolumn = $this->CurrCol;\n                            if ($this->AcceptPageBreak()) {\n                                $newpagestarted = true;\n                                $this->y = $y + $y0;\n\n                                // Move down to account for border-spacing or\n                                // extra half border width in case page breaks in middle\n                                if ($i > 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {\n                                    if ($table['borders_separate']) {\n                                        $adv = $table['border_spacing_V'] / 2;\n                                        // If table footer\n                                        if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {\n                                            $adv += ($table['padding']['B'] + $table['border_details']['B']['w']);\n                                        }\n                                    } else {\n                                        $maxbwtop = 0;\n                                        $maxbwbottom = 0;\n                                        if (!$this->simpleTables) {\n                                            if (!empty($tablefooter)) {\n                                                $maxbwbottom = $table['max_cell_border_width']['B'];\n                                            } else {\n                                                $brow = $i - 1;\n                                                for ($ctj = 0; $ctj < $numcols; $ctj++) {\n                                                    if (isset($cells[$brow][$ctj]) && $cells[$brow][$ctj]) {\n                                                        if ($this->packTableData) {\n                                                            [$bt, $br, $bb, $bl] = $this->_getBorderWidths($cells[$brow][$ctj]['borderbin']);\n                                                        } else {\n                                                            $bb = $cells[$brow][$ctj]['border_details']['B']['w'];\n                                                        }\n                                                        $maxbwbottom = max($maxbwbottom, $bb);\n                                                    }\n                                                }\n                                            }\n                                            if (!empty($tableheader)) {\n                                                $maxbwtop = $table['max_cell_border_width']['T'];\n                                            } else {\n                                                $trow = $i - 1;\n                                                for ($ctj = 0; $ctj < $numcols; $ctj++) {\n                                                    if (isset($cells[$trow][$ctj]) && $cells[$trow][$ctj]) {\n                                                        if ($this->packTableData) {\n                                                            [$bt, $br, $bb, $bl] = $this->_getBorderWidths($cells[$trow][$ctj]['borderbin']);\n                                                        } else {\n                                                            $bt = $cells[$trow][$ctj]['border_details']['T']['w'];\n                                                        }\n                                                        $maxbwtop = max($maxbwtop, $bt);\n                                                    }\n                                                }\n                                            }\n                                        } elseif ($this->simpleTables) {\n                                            $maxbwtop = $table['simple']['border_details']['T']['w'];\n                                            $maxbwbottom = $table['simple']['border_details']['B']['w'];\n                                        }\n                                        $adv = $maxbwbottom / 2;\n                                    }\n                                    $this->y += $adv;\n                                }\n\n                                // Rotated table split over pages - needs this->y for borders/backgrounds\n                                if ($i > 0 && $this->table_rotate && $level == 1) {\n                                    // \t\t$this->y = $y0 + $this->tbrot_w;\n                                }\n\n                                if ($this->tableClipPath) {\n                                    $this->writer->write(\"Q\");\n                                }\n\n                                $bx = $x0;\n                                $by = $y0;\n\n                                if ($table['borders_separate']) {\n                                    $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);\n                                    if ($tablestartpageno != $this->page) { // IF already broken across a previous pagebreak\n                                        $by += $table['max_cell_border_width']['T'] / 2;\n                                        if (empty($tableheader)) {\n                                            $by -= ($table['border_spacing_V'] / 2);\n                                        }\n                                    } else {\n                                        $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);\n                                    }\n                                } elseif ($tablestartpageno != $this->page && !empty($tableheader)) {\n                                    $by += $maxbwtop / 2;\n                                }\n\n                                $by -= $tableheaderadj;\n                                $bh = $this->y - $by + $tablefooteradj;\n                                if (!$table['borders_separate']) {\n                                    $bh -= $adv;\n                                }\n                                if ($split) {\n                                    $bw = 0;\n                                    for ($t = $startcol; $t < $numcols; $t++) {\n                                        if ($table['colPg'][$t] == $splitpg) {\n                                            $bw += $table['wc'][$t];\n                                        }\n                                        if ($table['colPg'][$t] > $splitpg) {\n                                            break;\n                                        }\n                                    }\n                                    if ($table['borders_separate']) {\n                                        if ($firstSpread) {\n                                            $bw += $table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'];\n                                        } else {\n                                            $bx += ($table['padding']['L'] + $table['border_details']['L']['w']);\n                                            $bw += $table['border_spacing_H'];\n                                        }\n                                        if ($finalSpread) {\n                                            $bw += $table['padding']['R'] + $table['border_details']['R']['w'] / 2 + $table['border_spacing_H'];\n                                        }\n                                    }\n                                } else {\n                                    $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n                                }\n\n                                if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tablefooter) && $i > 0 && $table['border_details']['B']['w']) {\n                                    $prevDrawColor = $this->DrawColor;\n                                    $lw = $this->LineWidth;\n                                    $this->SetLineWidth($this->splitTableBorderWidth);\n                                    $this->SetDColor($table['border_details']['B']['c']);\n                                    $this->SetLineJoin(0);\n                                    $this->SetLineCap(0);\n                                    $blx = $bx;\n                                    $blw = $bw;\n                                    if (!$table['borders_separate']) {\n                                        $blx -= ($table['max_cell_border_width']['L'] / 2);\n                                        $blw += ($table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2);\n                                    }\n                                    $this->Line($blx, $this->y + ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y + ($this->splitTableBorderWidth / 2));\n                                    $this->DrawColor = $prevDrawColor;\n                                    $this->writer->write($this->DrawColor);\n                                    $this->SetLineWidth($lw);\n                                    $this->SetLineJoin(2);\n                                    $this->SetLineCap(2);\n                                }\n\n                                if (!$this->ColActive && ($i > 0 || $j > 0)) {\n                                    if (isset($table['bgcolor'][-1])) {\n                                        $color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);\n                                        if ($color) {\n                                            if (!$table['borders_separate']) {\n                                                $bh -= $table['max_cell_border_width']['B'] / 2;\n                                            }\n                                            $this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];\n                                        }\n                                    }\n\n                                    /* -- BACKGROUNDS -- */\n                                    if (isset($table['gradient'])) {\n                                        $g = $this->gradient->parseBackgroundGradient($table['gradient']);\n                                        if ($g) {\n                                            $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                        }\n                                    }\n\n                                    if (isset($table['background-image'])) {\n                                        if ($table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {\n                                            $g = $this->gradient->parseMozGradient($table['background-image']['gradient']);\n                                            if ($g) {\n                                                $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                            }\n                                        } else {\n                                            $image_id = $table['background-image']['image_id'];\n                                            $orig_w = $table['background-image']['orig_w'];\n                                            $orig_h = $table['background-image']['orig_h'];\n                                            $x_pos = $table['background-image']['x_pos'];\n                                            $y_pos = $table['background-image']['y_pos'];\n                                            $x_repeat = $table['background-image']['x_repeat'];\n                                            $y_repeat = $table['background-image']['y_repeat'];\n                                            $resize = $table['background-image']['resize'];\n                                            $opacity = $table['background-image']['opacity'];\n                                            $itype = $table['background-image']['itype'];\n                                            $this->tableBackgrounds[$level * 9 + 2][] = ['x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];\n                                        }\n                                    }\n                                    /* -- END BACKGROUNDS -- */\n                                }\n\n                                // $this->AcceptPageBreak() has moved tablebuffer to $this->pages content\n                                if ($this->tableBackgrounds) {\n                                    $s = $this->PrintTableBackgrounds();\n                                    if ($this->bufferoutput) {\n                                        $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\\\1' . \"\\n\" . $s . \"\\n\", $this->headerbuffer);\n                                        $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', \" \", $this->headerbuffer);\n                                    } else {\n                                        $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\\\1' . \"\\n\" . $s . \"\\n\", $this->pages[$this->page]);\n                                        $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', \" \", $this->pages[$this->page]);\n                                    }\n                                    $this->tableBackgrounds = [];\n                                }\n\n                                if ($split) {\n                                    if ($i == 0 && $j == 0) {\n                                        $y0 = -1;\n                                    } elseif ($finalSpread) {\n                                        $splitpg = 0;\n                                        $startcol = 0;\n                                        $startrow = $i;\n                                    } else {\n                                        $splitpg++;\n                                        $startcol = $t;\n                                        $returny -= $y;\n                                    }\n                                    return [false, $startrow, $startcol, $splitpg, $returny, $y0];\n                                }\n\n                                $this->AddPage($this->CurOrientation);\n\n                                $this->writer->write('___TABLE___BACKGROUNDS' . $this->uniqstr);\n\n\n                                if ($this->tableClipPath) {\n                                    $this->writer->write($this->tableClipPath);\n                                }\n\n                                // Added to correct for OddEven Margins\n                                $x = $x + $this->MarginCorrection;\n                                $x0 = $x0 + $this->MarginCorrection;\n\n                                if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tableheader) && $i > 0 && $table['border_details']['T']['w']) {\n                                    $prevDrawColor = $this->DrawColor;\n                                    $lw = $this->LineWidth;\n                                    $this->SetLineWidth($this->splitTableBorderWidth);\n                                    $this->SetDColor($table['border_details']['T']['c']);\n                                    $this->SetLineJoin(0);\n                                    $this->SetLineCap(0);\n                                    $blx += $this->MarginCorrection;\n                                    $this->Line($blx, $this->y - ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y - ($this->splitTableBorderWidth / 2));\n                                    $this->DrawColor = $prevDrawColor;\n                                    $this->writer->write($this->DrawColor);\n                                    $this->SetLineWidth($lw);\n                                    $this->SetLineJoin(2);\n                                    $this->SetLineCap(2);\n                                }\n\n                                // Move down to account for half of top border-spacing or\n                                // extra half border width in case page was broken in middle\n                                if ($i > 0 && !$this->table_rotate && $level == 1 && $table['headernrows'] == 0) {\n                                    if ($table['borders_separate']) {\n                                        $adv = $table['border_spacing_V'] / 2;\n                                    } else {\n                                        $maxbwtop = 0;\n                                        for ($ctj = 0; $ctj < $numcols; $ctj++) {\n                                            if (isset($cells[$i][$ctj]) && $cells[$i][$ctj]) {\n                                                if (!$this->simpleTables) {\n                                                    if ($this->packTableData) {\n                                                        [$bt, $br, $bb, $bl] = $this->_getBorderWidths($cells[$i][$ctj]['borderbin']);\n                                                    } else {\n                                                        $bt = $cells[$i][$ctj]['border_details']['T']['w'];\n                                                    }\n                                                    $maxbwtop = max($maxbwtop, $bt);\n                                                } elseif ($this->simpleTables) {\n                                                    $maxbwtop = max($maxbwtop, $table['simple']['border_details']['T']['w']);\n                                                }\n                                            }\n                                        }\n                                        $adv = $maxbwtop / 2;\n                                    }\n                                    $this->y += $adv;\n                                }\n\n\n                                if ($this->table_rotate) {\n                                    $this->tbrot_x0 = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];\n                                    if ($table['borders_separate']) {\n                                        $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;\n                                    } else {\n                                        $this->tbrot_h = $table['margin']['T'] + $table['max_cell_border_width']['T'];\n                                    }\n                                    $this->tbrot_y0 = $this->y;\n                                    $pagetrigger = $y0 - $tableheaderadj + ($this->blk[$this->blklvl]['inner_width']);\n                                } else {\n                                    $pagetrigger = $this->PageBreakTrigger;\n                                }\n\n                                if ($this->kwt_saved && $level == 1) {\n                                    $this->kwt_moved = true;\n                                }\n\n\n                                if (!empty($tableheader)) {\n                                    $ya = $this->y;\n                                    $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);\n                                    if ($this->table_rotate) {\n                                        $this->tbrot_h = $this->y - $ya;\n                                    }\n                                    $tableheaderadj = $this->y - $ya;\n                                } elseif ($i == 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {\n                                    // Advance down page\n                                    if ($table['borders_separate']) {\n                                        $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];\n                                    } else {\n                                        $adv = $table['max_cell_border_width']['T'] / 2;\n                                    }\n                                    if ($adv) {\n                                        if ($this->table_rotate) {\n                                            $this->y += ($adv);\n                                        } else {\n                                            $this->DivLn($adv, $this->blklvl, true);\n                                        }\n                                    }\n                                }\n\n                                $outerfilled = 0;\n                                $y = $y0 = $this->y;\n                            }\n\n                            /* -- COLUMNS -- */\n                            // COLS\n                            // COLUMN CHANGE\n                            if ($this->CurrCol != $oldcolumn) {\n                                // Added to correct for Columns\n                                $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n                                $x0 += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);\n                                if ($this->CurrCol == 0) {  // just added a page - possibly with tableheader\n                                    $y0 = $this->y;  // this->y0 is global used by Columns - $y0 is internal to tablewrite\n                                } else {\n                                    $y0 = $this->y0;  // this->y0 is global used by Columns - $y0 is internal to tablewrite\n                                }\n                                $y = $y0;\n                                $outerfilled = 0;\n                                if ($this->CurrCol != 0 && ($this->keepColumns && $this->ColActive) && !empty($tableheader) && $i > 0) {\n                                    $this->x = $x;\n                                    $this->y = $y;\n                                    $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);\n                                    $y0 = $y = $this->y;\n                                }\n                            }\n                            /* -- END COLUMNS -- */\n                        }\n                        $skippage = true;\n                    }\n\n                    $this->x = $x;\n                    $this->y = $y;\n\n                    if ($this->kwt_saved && $level == 1) {\n                        $this->printkwtbuffer();\n                        $x0 = $x = $this->x;\n                        $y0 = $y = $this->y;\n                        $this->kwt_moved = false;\n                        $this->kwt_saved = false;\n                    }\n\n\n                    // Set the Page & Column where table actually starts\n                    if ($i == 0 && $j == 0 && $level == 1) {\n                        if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {    // EVEN\n                            $tablestartpage = 'EVEN';\n                        } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) {    // ODD\n                            $tablestartpage = 'ODD';\n                        } else {\n                            $tablestartpage = '';\n                        }\n                        $tablestartpageno = $this->page;\n                        if ($this->ColActive) {\n                            $tablestartcolumn = $this->CurrCol;\n                        } // *COLUMNS*\n                    }\n\n                    // ALIGN\n                    $align = $cell['a'];\n\n                    /* -- COLUMNS -- */\n                    // If outside columns, this is done in PaintDivBB\n                    if ($this->ColActive) {\n                        // OUTER FILL BGCOLOR of DIVS\n                        if ($this->blklvl > 0 && ($j == 0) && !$this->table_rotate && $level == 1) {\n                            $firstblockfill = $this->GetFirstBlockFill();\n                            if ($firstblockfill && $this->blklvl >= $firstblockfill) {\n                                $divh = $maxrowheight;\n                                // Last row\n                                if ((!isset($cell['rowspan']) && $i == $numrows - 1) || (isset($cell['rowspan']) && (($i == $numrows - 1 && $cell['rowspan'] < 2) || ($cell['rowspan'] > 1 && ($i + $cell['rowspan'] - 1) == $numrows - 1)))) {\n                                    if ($table['borders_separate']) {\n                                        $adv = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;\n                                    } else {\n                                        $adv = $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;\n                                    }\n                                    $divh += $adv;  // last row: fill bottom half of bottom border (y advanced at end)\n                                }\n\n                                if (($this->y + $divh) > $outerfilled) { // if not already painted by previous rowspan\n                                    $bak_x = $this->x;\n                                    $bak_y = $this->y;\n                                    if ($outerfilled > $this->y) {\n                                        $divh = ($this->y + $divh) - $outerfilled;\n                                        $this->y = $outerfilled;\n                                    }\n\n                                    $this->DivLn($divh, -3, false);\n                                    $outerfilled = $this->y + $divh;\n                                    // Reset current block fill\n                                    $bcor = $this->blk[$this->blklvl]['bgcolorarray'];\n                                    if ($bcor) {\n                                        $this->SetFColor($bcor);\n                                    }\n                                    $this->x = $bak_x;\n                                    $this->y = $bak_y;\n                                }\n                            }\n                        }\n                    }\n\n                    // TABLE BACKGROUND FILL BGCOLOR - for cellSpacing\n                    if ($this->ColActive) {\n                        if ($table['borders_separate']) {\n                            $fill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;\n                            if ($fill) {\n                                $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);\n                                if ($color) {\n                                    $xadj = ($table['border_spacing_H'] / 2);\n                                    $yadj = ($table['border_spacing_V'] / 2);\n                                    $wadj = $table['border_spacing_H'];\n                                    $hadj = $table['border_spacing_V'];\n                                    if ($i == 0) {  // Top\n                                        $yadj += $table['padding']['T'] + $table['border_details']['T']['w'];\n                                        $hadj += $table['padding']['T'] + $table['border_details']['T']['w'];\n                                    }\n                                    if ($j == 0) {  // Left\n                                        $xadj += $table['padding']['L'] + $table['border_details']['L']['w'];\n                                        $wadj += $table['padding']['L'] + $table['border_details']['L']['w'];\n                                    }\n                                    if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) { // Bottom\n                                        $hadj += $table['padding']['B'] + $table['border_details']['B']['w'];\n                                    }\n                                    if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols) || (!isset($cell['colspan']) && ($j + 1) == $numcols)) { // Right\n                                        $wadj += $table['padding']['R'] + $table['border_details']['R']['w'];\n                                    }\n                                    $this->SetFColor($color);\n                                    $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');\n                                }\n                            }\n                        }\n                    }\n                    /* -- END COLUMNS -- */\n\n                    if ($table['empty_cells'] != 'hide' || !empty($cell['textbuffer']) || (isset($cell['nestedcontent']) && $cell['nestedcontent']) || !$table['borders_separate']) {\n                        $paintcell = true;\n                    } else {\n                        $paintcell = false;\n                    }\n\n                    // Set Borders\n                    $bord = 0;\n                    $bord_det = [];\n\n                    if (!$this->simpleTables) {\n                        if ($this->packTableData) {\n                            $c = $this->_unpackCellBorder($cell['borderbin']);\n                            $bord = $c['border'];\n                            $bord_det = $c['border_details'];\n                        } else {\n                            $bord = $cell['border'];\n                            $bord_det = $cell['border_details'];\n                        }\n                    } elseif ($this->simpleTables) {\n                        $bord = $table['simple']['border'];\n                        $bord_det = $table['simple']['border_details'];\n                    }\n\n                    // TABLE ROW OR CELL FILL BGCOLOR\n                    $fill = 0;\n                    if (isset($cell['bgcolor']) && $cell['bgcolor'] && $cell['bgcolor'] != 'transparent') {\n                        $fill = $cell['bgcolor'];\n                        $leveladj = 6;\n                    } elseif (isset($table['bgcolor'][$i]) && $table['bgcolor'][$i] && $table['bgcolor'][$i] != 'transparent') { // Row color\n                        $fill = $table['bgcolor'][$i];\n                        $leveladj = 3;\n                    }\n                    if ($fill && $paintcell) {\n                        $color = $this->colorConverter->convert($fill, $this->PDFAXwarnings);\n                        if ($color) {\n                            if ($table['borders_separate']) {\n                                if ($this->ColActive) {\n                                    $this->SetFColor($color);\n                                    $this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F');\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color];\n                                }\n                            } else {\n                                if ($this->ColActive) {\n                                    $this->SetFColor($color);\n                                    $this->Rect($x, $y, $w, $h, 'F');\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + $leveladj][] = ['gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color];\n                                }\n                            }\n                        }\n                    }\n\n                    /* -- BACKGROUNDS -- */\n                    if (isset($cell['gradient']) && $cell['gradient'] && $paintcell) {\n                        $g = $this->gradient->parseBackgroundGradient($cell['gradient']);\n                        if ($g) {\n                            if ($table['borders_separate']) {\n                                $px = $x + ($table['border_spacing_H'] / 2);\n                                $py = $y + ($table['border_spacing_V'] / 2);\n                                $pw = $w - $table['border_spacing_H'];\n                                $ph = $h - $table['border_spacing_V'];\n                            } else {\n                                $px = $x;\n                                $py = $y;\n                                $pw = $w;\n                                $ph = $h;\n                            }\n                            if ($this->ColActive) {\n                                $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);\n                            } else {\n                                $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                            }\n                        }\n                    }\n\n                    if (isset($cell['background-image']) && $paintcell) {\n                        if (isset($cell['background-image']['gradient']) && $cell['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $cell['background-image']['gradient'])) {\n                            $g = $this->gradient->parseMozGradient($cell['background-image']['gradient']);\n                            if ($g) {\n                                if ($table['borders_separate']) {\n                                    $px = $x + ($table['border_spacing_H'] / 2);\n                                    $py = $y + ($table['border_spacing_V'] / 2);\n                                    $pw = $w - $table['border_spacing_H'];\n                                    $ph = $h - $table['border_spacing_V'];\n                                } else {\n                                    $px = $x;\n                                    $py = $y;\n                                    $pw = $w;\n                                    $ph = $h;\n                                }\n                                if ($this->ColActive) {\n                                    $this->gradient->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + 7][] = ['gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                }\n                            }\n                        } elseif (isset($cell['background-image']['image_id']) && $cell['background-image']['image_id']) { // Background pattern\n                            $n = count($this->patterns) + 1;\n                            if ($table['borders_separate']) {\n                                $px = $x + ($table['border_spacing_H'] / 2);\n                                $py = $y + ($table['border_spacing_V'] / 2);\n                                $pw = $w - $table['border_spacing_H'];\n                                $ph = $h - $table['border_spacing_V'];\n                            } else {\n                                $px = $x;\n                                $py = $y;\n                                $pw = $w;\n                                $ph = $h;\n                            }\n                            if ($this->ColActive) {\n                                [$orig_w, $orig_h, $x_repeat, $y_repeat] = $this->_resizeBackgroundImage($cell['background-image']['orig_w'], $cell['background-image']['orig_h'], $pw, $ph, $cell['background-image']['resize'], $cell['background-image']['x_repeat'], $cell['background-image']['y_repeat']);\n                                $this->patterns[$n] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $cell['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $cell['background-image']['x_pos'], 'y_pos' => $cell['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat];\n                                if ($cell['background-image']['opacity'] > 0 && $cell['background-image']['opacity'] < 1) {\n                                    $opac = $this->SetAlpha($cell['background-image']['opacity'], 'Normal', true);\n                                } else {\n                                    $opac = '';\n                                }\n                                $this->writer->write(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * Mpdf::SCALE, ($this->h - $py) * Mpdf::SCALE, $pw * Mpdf::SCALE, -$ph * Mpdf::SCALE));\n                            } else {\n                                $image_id = $cell['background-image']['image_id'];\n                                $orig_w = $cell['background-image']['orig_w'];\n                                $orig_h = $cell['background-image']['orig_h'];\n                                $x_pos = $cell['background-image']['x_pos'];\n                                $y_pos = $cell['background-image']['y_pos'];\n                                $x_repeat = $cell['background-image']['x_repeat'];\n                                $y_repeat = $cell['background-image']['y_repeat'];\n                                $resize = $cell['background-image']['resize'];\n                                $opacity = $cell['background-image']['opacity'];\n                                $itype = $cell['background-image']['itype'];\n                                $this->tableBackgrounds[$level * 9 + 8][] = ['x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];\n                            }\n                        }\n                    }\n                    /* -- END BACKGROUNDS -- */\n\n                    if (isset($cell['colspan']) && $cell['colspan'] > 1) {\n                        $ccolsp = $cell['colspan'];\n                    } else {\n                        $ccolsp = 1;\n                    }\n                    if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {\n                        $crowsp = $cell['rowspan'];\n                    } else {\n                        $crowsp = 1;\n                    }\n\n\n                    // but still need to do this for repeated headers...\n                    if (!$table['borders_separate'] && $this->tabletheadjustfinished && !$this->simpleTables) {\n                        if (isset($table['topntail']) && $table['topntail']) {\n                            $bord_det['T'] = $this->border_details($table['topntail']);\n                            $bord_det['T']['w'] /= $this->shrin_k;\n                            $this->setBorder($bord, Border::TOP);\n                        }\n                        if (isset($table['thead-underline']) && $table['thead-underline']) {\n                            $bord_det['T'] = $this->border_details($table['thead-underline']);\n                            $bord_det['T']['w'] /= $this->shrin_k;\n                            $this->setBorder($bord, Border::TOP);\n                        }\n                    }\n\n\n                    // Get info of first row ==>> table header\n                    // Use > 1 row if THEAD\n                    if (isset($table['is_thead'][$i]) && $table['is_thead'][$i] && $level == 1) {\n                        if ($j == 0) {\n                            $tableheaderrowheight += $table['hr'][$i];\n                        }\n                        $tableheader[$i][0]['trbackground-images'] = (isset($table['trbackground-images'][$i]) ? $table['trbackground-images'][$i] : null);\n                        $tableheader[$i][0]['trgradients'] = (isset($table['trgradients'][$i]) ? $table['trgradients'][$i] : null);\n                        $tableheader[$i][0]['trbgcolor'] = (isset($table['bgcolor'][$i]) ? $table['bgcolor'][$i] : null);\n                        $tableheader[$i][$j]['x'] = $x;\n                        $tableheader[$i][$j]['y'] = $y;\n                        $tableheader[$i][$j]['h'] = $h;\n                        $tableheader[$i][$j]['w'] = $w;\n                        if (isset($cell['textbuffer'])) {\n                            $tableheader[$i][$j]['textbuffer'] = $cell['textbuffer'];\n                        } else {\n                            $tableheader[$i][$j]['textbuffer'] = '';\n                        }\n                        $tableheader[$i][$j]['a'] = $cell['a'];\n                        $tableheader[$i][$j]['R'] = $cell['R'];\n\n                        $tableheader[$i][$j]['va'] = $cell['va'];\n                        $tableheader[$i][$j]['mih'] = $cell['mih'];\n                        $tableheader[$i][$j]['gradient'] = (isset($cell['gradient']) ? $cell['gradient'] : null); // *BACKGROUNDS*\n                        $tableheader[$i][$j]['background-image'] = (isset($cell['background-image']) ? $cell['background-image'] : null); // *BACKGROUNDS*\n                        $tableheader[$i][$j]['rowspan'] = (isset($cell['rowspan']) ? $cell['rowspan'] : null);\n                        $tableheader[$i][$j]['colspan'] = (isset($cell['colspan']) ? $cell['colspan'] : null);\n                        $tableheader[$i][$j]['bgcolor'] = $cell['bgcolor'];\n\n                        if (!$this->simpleTables) {\n                            $tableheader[$i][$j]['border'] = $bord;\n                            $tableheader[$i][$j]['border_details'] = $bord_det;\n                        } elseif ($this->simpleTables) {\n                            $tableheader[$i][$j]['border'] = $table['simple']['border'];\n                            $tableheader[$i][$j]['border_details'] = $table['simple']['border_details'];\n                        }\n                        $tableheader[$i][$j]['padding'] = $cell['padding'];\n                        if (isset($cell['direction'])) {\n                            $tableheader[$i][$j]['direction'] = $cell['direction'];\n                        }\n                        if (isset($cell['cellLineHeight'])) {\n                            $tableheader[$i][$j]['cellLineHeight'] = $cell['cellLineHeight'];\n                        }\n                        if (isset($cell['cellLineStackingStrategy'])) {\n                            $tableheader[$i][$j]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];\n                        }\n                        if (isset($cell['cellLineStackingShift'])) {\n                            $tableheader[$i][$j]['cellLineStackingShift'] = $cell['cellLineStackingShift'];\n                        }\n                    }\n\n                    // CELL BORDER\n                    if ($bord) {\n                        if ($table['borders_separate'] && $paintcell) {\n                            $this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($bord_det['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($bord_det['T']['w'] / 2), $w - $table['border_spacing_H'] - ($bord_det['L']['w'] / 2) - ($bord_det['R']['w'] / 2), $h - $table['border_spacing_V'] - ($bord_det['T']['w'] / 2) - ($bord_det['B']['w'] / 2), $bord, $bord_det, false, $table['borders_separate']);\n                        } elseif (!$table['borders_separate']) {\n                            $this->_tableRect($x, $y, $w, $h, $bord, $bord_det, true, $table['borders_separate']);  // true causes buffer\n                        }\n                    }\n\n                    // VERTICAL ALIGN\n                    if ($cell['R'] && intval($cell['R']) > 0 && intval($cell['R']) < 90 && isset($cell['va']) && $cell['va'] != 'B') {\n                        $cell['va'] = 'B';\n                    }\n                    if (!isset($cell['va']) || $cell['va'] == 'M') {\n                        $this->y += ($h - $cell['mih']) / 2;\n                    } elseif (isset($cell['va']) && $cell['va'] == 'B') {\n                        $this->y += $h - $cell['mih'];\n                    }\n\n                    // NESTED CONTENT\n                    // TEXT (and nested tables)\n\n                    $this->divwidth = $w;\n                    if (!empty($cell['textbuffer'])) {\n                        $this->cellTextAlign = $align;\n                        $this->cellLineHeight = $cell['cellLineHeight'];\n                        $this->cellLineStackingStrategy = $cell['cellLineStackingStrategy'];\n                        $this->cellLineStackingShift = $cell['cellLineStackingShift'];\n                        if ($level == 1) {\n                            if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {\n                                if (preg_match('/{colsum([0-9]*)[_]*}/', $cell['textbuffer'][0][0], $m)) {\n                                    $rep = sprintf(\"%01.\" . intval($m[1]) . \"f\", $this->colsums[$j]);\n                                    $cell['textbuffer'][0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $cell['textbuffer'][0][0]);\n                                }\n                            } elseif (!isset($table['is_thead'][$i])) {\n                                if (isset($this->colsums[$j])) {\n                                    $this->colsums[$j] += $this->toFloat($cell['textbuffer'][0][0]);\n                                } else {\n                                    $this->colsums[$j] = $this->toFloat($cell['textbuffer'][0][0]);\n                                }\n                            }\n                        }\n                        $opy = $this->y;\n                        // mPDF ITERATION\n                        if ($this->iterationCounter) {\n                            foreach ($cell['textbuffer'] as $k => $t) {\n                                if (preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {\n                                    $vname = '__' . $m[1] . '_';\n                                    if (!isset($this->$vname)) {\n                                        $this->$vname = 1;\n                                    } else {\n                                        $this->$vname++;\n                                    }\n                                    $cell['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $cell['textbuffer'][$k][0]);\n                                }\n                            }\n                        }\n\n\n                        if ($cell['R']) {\n                            $cellPtSize = $cell['textbuffer'][0][11] / $this->shrin_k;\n                            if (!$cellPtSize) {\n                                $cellPtSize = $this->default_font_size;\n                            }\n                            $cellFontHeight = ($cellPtSize / Mpdf::SCALE);\n                            $opx = $this->x;\n                            $angle = intval($cell['R']);\n                            // Only allow 45 to 89 degrees (when bottom-aligned) or exactly 90 or -90\n                            if ($angle > 90) {\n                                $angle = 90;\n                            } elseif ($angle > 0 && $angle < 45) {\n                                $angle = 45;\n                            } elseif ($angle < 0) {\n                                $angle = -90;\n                            }\n                            $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);\n                            if (isset($cell['a']) && $cell['a'] == 'R') {\n                                $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($cell['padding']['R'] + ($table['border_spacing_H'] / 2));\n                            } elseif (!isset($cell['a']) || $cell['a'] == 'C') {\n                                $this->x += ($w / 2) + ($offset);\n                            } else {\n                                $this->x += ($offset) + ($cellFontHeight / 3) + ($cell['padding']['L'] + ($table['border_spacing_H'] / 2));\n                            }\n                            $str = '';\n                            foreach ($cell['textbuffer'] as $t) {\n                                $str .= $t[0] . ' ';\n                            }\n                            $str = rtrim($str);\n                            if (!isset($cell['va']) || $cell['va'] == 'M') {\n                                $this->y -= ($h - $cell['mih']) / 2; // Undo what was added earlier VERTICAL ALIGN\n                                if ($angle > 0) {\n                                    $this->y += (($h - $cell['mih']) / 2) + $cell['padding']['T'] + ($cell['mih'] - ($cell['padding']['T'] + $cell['padding']['B']));\n                                } elseif ($angle < 0) {\n                                    $this->y += (($h - $cell['mih']) / 2) + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));\n                                }\n                            } elseif (isset($cell['va']) && $cell['va'] == 'B') {\n                                $this->y -= $h - $cell['mih']; // Undo what was added earlier VERTICAL ALIGN\n                                if ($angle > 0) {\n                                    $this->y += $h - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));\n                                } elseif ($angle < 0) {\n                                    $this->y += $h - $cell['mih'] + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));\n                                }\n                            } elseif (isset($cell['va']) && $cell['va'] == 'T') {\n                                if ($angle > 0) {\n                                    $this->y += $cell['mih'] - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));\n                                } elseif ($angle < 0) {\n                                    $this->y += ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));\n                                }\n                            }\n                            $this->Rotate($angle, $this->x, $this->y);\n                            $s_fs = $this->FontSizePt;\n                            $s_f = $this->FontFamily;\n                            $s_st = $this->FontStyle;\n                            if (!empty($cell['textbuffer'][0][3])) { // Font Color\n                                $cor = $cell['textbuffer'][0][3];\n                                $this->SetTColor($cor);\n                            }\n                            $this->SetFont($cell['textbuffer'][0][4], $cell['textbuffer'][0][2], $cellPtSize, true, true);\n\n                            $this->magic_reverse_dir($str, $this->directionality, $cell['textbuffer'][0][18]);\n                            $this->Text($this->x, $this->y, $str, $cell['textbuffer'][0][18], $cell['textbuffer'][0][8]); // textvar\n                            $this->Rotate(0);\n                            $this->SetFont($s_f, $s_st, $s_fs, true, true);\n                            $this->SetTColor(0);\n                            $this->x = $opx;\n                        } else {\n                            if (!$this->simpleTables) {\n                                if ($bord_det) {\n                                    $btlw = $bord_det['L']['w'];\n                                    $btrw = $bord_det['R']['w'];\n                                    $bttw = $bord_det['T']['w'];\n                                } else {\n                                    $btlw = 0;\n                                    $btrw = 0;\n                                    $bttw = 0;\n                                }\n                                if ($table['borders_separate']) {\n                                    $xadj = $btlw + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);\n                                    $wadj = $btlw + $btrw + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];\n                                    $yadj = $bttw + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);\n                                } else {\n                                    $xadj = $btlw / 2 + $cell['padding']['L'];\n                                    $wadj = ($btlw + $btrw) / 2 + $cell['padding']['L'] + $cell['padding']['R'];\n                                    $yadj = $bttw / 2 + $cell['padding']['T'];\n                                }\n                            } elseif ($this->simpleTables) {\n                                if ($table['borders_separate']) { // NB twice border width\n                                    $xadj = $table['simple']['border_details']['L']['w'] + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);\n                                    $wadj = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];\n                                    $yadj = $table['simple']['border_details']['T']['w'] + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);\n                                } else {\n                                    $xadj = $table['simple']['border_details']['L']['w'] / 2 + $cell['padding']['L'];\n                                    $wadj = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + $cell['padding']['L'] + $cell['padding']['R'];\n                                    $yadj = $table['simple']['border_details']['T']['w'] / 2 + $cell['padding']['T'];\n                                }\n                            }\n                            $this->decimal_offset = 0;\n                            if (substr($cell['a'], 0, 1) == 'D') {\n                                if (isset($cell['colspan']) && $cell['colspan'] > 1) {\n                                    $this->cellTextAlign = $c['a'] = substr($cell['a'], 2, 1);\n                                } else {\n                                    $smax = $table['decimal_align'][$j]['maxs0'];\n                                    $d_content = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'];\n                                    $this->decimal_offset = $smax;\n                                    $extra = ($w - $d_content - $wadj);\n                                    if ($extra > 0) {\n                                        if (substr($cell['a'], 2, 1) == 'R') {\n                                            $this->decimal_offset += $extra;\n                                        } elseif (substr($cell['a'], 2, 1) == 'C') {\n                                            $this->decimal_offset += ($extra) / 2;\n                                        }\n                                    }\n                                }\n                            }\n                            $this->divwidth = $w - $wadj;\n                            if ($this->divwidth == 0) {\n                                $this->divwidth = 0.0001;\n                            }\n                            $this->x += $xadj;\n                            $this->y += $yadj;\n                            $this->printbuffer($cell['textbuffer'], '', true, false, $cell['direction']);\n                        }\n                        $this->y = $opy;\n                    }\n\n                    /* -- BACKGROUNDS -- */\n                    if (!$this->ColActive) {\n                        if (isset($table['trgradients'][$i]) && ($j == 0 || $table['borders_separate'])) {\n                            $g = $this->gradient->parseBackgroundGradient($table['trgradients'][$i]);\n                            if ($g) {\n                                $gx = $x0;\n                                $gy = $y;\n                                $gh = $h;\n                                $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n                                if ($table['borders_separate']) {\n                                    $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);\n                                    $clx = $x + ($table['border_spacing_H'] / 2);\n                                    $cly = $y + ($table['border_spacing_V'] / 2);\n                                    $clw = $w - $table['border_spacing_H'];\n                                    $clh = $h - $table['border_spacing_V'];\n                                    // Set clipping path\n                                    $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6\n                                    $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                }\n                            }\n                        }\n                        if (isset($table['trbackground-images'][$i]) && ($j == 0 || $table['borders_separate'])) {\n                            if (isset($table['trbackground-images'][$i]['gradient']) && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['trbackground-images'][$i]['gradient'])) {\n                                $g = $this->gradient->parseMozGradient($table['trbackground-images'][$i]['gradient']);\n                                if ($g) {\n                                    $gx = $x0;\n                                    $gy = $y;\n                                    $gh = $h;\n                                    $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n                                    if ($table['borders_separate']) {\n                                        $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);\n                                        $clx = $x + ($table['border_spacing_H'] / 2);\n                                        $cly = $y + ($table['border_spacing_V'] / 2);\n                                        $clw = $w - $table['border_spacing_H'];\n                                        $clh = $h - $table['border_spacing_V'];\n                                        // Set clipping path\n                                        $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6\n                                        $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s];\n                                    } else {\n                                        $this->tableBackgrounds[$level * 9 + 4][] = ['gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                                    }\n                                }\n                            } else {\n                                $image_id = $table['trbackground-images'][$i]['image_id'];\n                                $orig_w = $table['trbackground-images'][$i]['orig_w'];\n                                $orig_h = $table['trbackground-images'][$i]['orig_h'];\n                                $x_pos = $table['trbackground-images'][$i]['x_pos'];\n                                $y_pos = $table['trbackground-images'][$i]['y_pos'];\n                                $x_repeat = $table['trbackground-images'][$i]['x_repeat'];\n                                $y_repeat = $table['trbackground-images'][$i]['y_repeat'];\n                                $resize = $table['trbackground-images'][$i]['resize'];\n                                $opacity = $table['trbackground-images'][$i]['opacity'];\n                                $itype = $table['trbackground-images'][$i]['itype'];\n                                $clippath = '';\n                                $gx = $x0;\n                                $gy = $y;\n                                $gh = $h;\n                                $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n                                if ($table['borders_separate']) {\n                                    $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);\n                                    $clx = $x + ($table['border_spacing_H'] / 2);\n                                    $cly = $y + ($table['border_spacing_V'] / 2);\n                                    $clw = $w - $table['border_spacing_H'];\n                                    $clh = $h - $table['border_spacing_V'];\n                                    // Set clipping path\n                                    $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6\n                                    $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];\n                                } else {\n                                    $this->tableBackgrounds[$level * 9 + 5][] = ['x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];\n                                }\n                            }\n                        }\n                    }\n\n                    /* -- END BACKGROUNDS -- */\n\n                    // TABLE BORDER - if separate\n                    if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {\n                        $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);\n                        $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);\n                        $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);\n                        $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);\n                        $tbx = $x;\n                        $tby = $y;\n                        $tbw = $w;\n                        $tbh = $h;\n                        $tab_bord = 0;\n\n                        $corner = '';\n                        if ($i == 0) {  // Top\n                            $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);\n                            $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::TOP);\n                            $corner .= 'T';\n                        }\n                        if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows)) { // Bottom\n                            $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::BOTTOM);\n                            $corner .= 'B';\n                        }\n                        if ($j == 0) {  // Left\n                            $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);\n                            $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::LEFT);\n                            $corner .= 'L';\n                        }\n                        if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols)) { // Right\n                            $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);\n                            $this->setBorder($tab_bord, Border::RIGHT);\n                            $corner .= 'R';\n                        }\n                        $this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']);\n                    }\n\n                    unset($cell);\n                    // Reset values\n                    $this->Reset();\n                }//end of (if isset(cells)...)\n            }// end of columns\n\n            $newpagestarted = false;\n            $this->tabletheadjustfinished = false;\n\n            /* -- COLUMNS -- */\n            if ($this->ColActive) {\n                if (!$this->table_keep_together && $i < $numrows - 1 && $level == 1) {\n                    $this->breakpoints[$this->CurrCol][] = $y + $h;\n                } // mPDF 6\n                if (count($this->cellBorderBuffer)) {\n                    $this->printcellbuffer();\n                }\n            }\n            /* -- END COLUMNS -- */\n\n            if ($i == $numrows - 1) {\n                $this->y = $y + $h;\n            } // last row jump (update this->y position)\n            if ($this->table_rotate && $level == 1) {\n                $this->tbrot_h += $h;\n            }\n        } // end of rows\n\n        if (count($this->cellBorderBuffer)) {\n            $this->printcellbuffer();\n        }\n\n\n        if ($this->tableClipPath) {\n            $this->writer->write(\"Q\");\n        }\n        $this->tableClipPath = '';\n\n        // Advance down page by half width of bottom border\n        if ($table['borders_separate']) {\n            $this->y += $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;\n        } else {\n            $this->y += $table['max_cell_border_width']['B'] / 2;\n        }\n\n        if ($table['borders_separate'] && $level == 1) {\n            $this->tbrot_h += $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;\n        } elseif ($level == 1) {\n            $this->tbrot_h += $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;\n        }\n\n        $bx = $x0;\n        $by = $y0;\n        if ($table['borders_separate']) {\n            $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);\n            if ($tablestartpageno != $this->page) { // IF broken across page\n                $by += $table['max_cell_border_width']['T'] / 2;\n                if (empty($tableheader)) {\n                    $by -= ($table['border_spacing_V'] / 2);\n                }\n            } elseif ($split && $startrow > 0 && empty($tableheader)) {\n                $by -= ($table['border_spacing_V'] / 2);\n            } else {\n                $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);\n            }\n        } elseif ($tablestartpageno != $this->page && !empty($tableheader)) {\n            $by += $maxbwtop / 2;\n        }\n        $by -= $tableheaderadj;\n        $bh = $this->y - $by;\n        if (!$table['borders_separate']) {\n            $bh -= $table['max_cell_border_width']['B'] / 2;\n        }\n\n        if ($split) {\n            $bw = 0;\n            $finalSpread = true;\n            for ($t = $startcol; $t < $numcols; $t++) {\n                if ($table['colPg'][$t] == $splitpg) {\n                    $bw += $table['wc'][$t];\n                }\n                if ($table['colPg'][$t] > $splitpg) {\n                    $finalSpread = false;\n                    break;\n                }\n            }\n            if ($startcol == 0) {\n                $firstSpread = true;\n            } else {\n                $firstSpread = false;\n            }\n            if ($table['borders_separate']) {\n                $bw += $table['border_spacing_H'];\n                if ($firstSpread) {\n                    $bw += $table['padding']['L'] + $table['border_details']['L']['w'];\n                } else {\n                    $bx += ($table['padding']['L'] + $table['border_details']['L']['w']);\n                }\n                if ($finalSpread) {\n                    $bw += $table['padding']['R'] + $table['border_details']['R']['w'];\n                }\n            }\n        } else {\n            $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];\n        }\n\n        if (!$this->ColActive) {\n            if (isset($table['bgcolor'][-1])) {\n                $color = $this->colorConverter->convert($table['bgcolor'][-1], $this->PDFAXwarnings);\n                if ($color) {\n                    $this->tableBackgrounds[$level * 9][] = ['gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color];\n                }\n            }\n\n            /* -- BACKGROUNDS -- */\n            if (isset($table['gradient'])) {\n                $g = $this->gradient->parseBackgroundGradient($table['gradient']);\n                if ($g) {\n                    $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                }\n            }\n\n            if (isset($table['background-image'])) {\n                if (isset($table['background-image']['gradient']) && $table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {\n                    $g = $this->gradient->parseMozGradient($table['background-image']['gradient']);\n                    if ($g) {\n                        $this->tableBackgrounds[$level * 9 + 1][] = ['gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => ''];\n                    }\n                } else {\n                    $image_id = $table['background-image']['image_id'];\n                    $orig_w = $table['background-image']['orig_w'];\n                    $orig_h = $table['background-image']['orig_h'];\n                    $x_pos = $table['background-image']['x_pos'];\n                    $y_pos = $table['background-image']['y_pos'];\n                    $x_repeat = $table['background-image']['x_repeat'];\n                    $y_repeat = $table['background-image']['y_repeat'];\n                    $resize = $table['background-image']['resize'];\n                    $opacity = $table['background-image']['opacity'];\n                    $itype = $table['background-image']['itype'];\n                    $this->tableBackgrounds[$level * 9 + 2][] = ['x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype];\n                }\n            }\n            /* -- END BACKGROUNDS -- */\n        }\n\n        if ($this->tableBackgrounds && $level == 1) {\n            $s = $this->PrintTableBackgrounds();\n            if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {\n                $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\\\1' . \"\\n\" . $s . \"\\n\", $this->tablebuffer);\n                if ($level == 1) {\n                    $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', \" \", $this->tablebuffer);\n                }\n            } elseif ($this->bufferoutput) {\n                $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\\\1' . \"\\n\" . $s . \"\\n\", $this->headerbuffer);\n                if ($level == 1) {\n                    $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', \" \", $this->headerbuffer);\n                }\n            } else {\n                $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\\\1' . \"\\n\" . $s . \"\\n\", $this->pages[$this->page]);\n                if ($level == 1) {\n                    $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', \" \", $this->pages[$this->page]);\n                }\n            }\n            $this->tableBackgrounds = [];\n        }\n\n\n        // TABLE BOTTOM MARGIN\n        if ($table['margin']['B']) {\n            if (!$this->table_rotate && $level == 1) {\n                $this->DivLn($table['margin']['B'], $this->blklvl, true);  // collapsible\n            } else {\n                $this->y += ($table['margin']['B']);\n            }\n        }\n\n        if ($this->ColActive && $level == 1) {\n            $this->breakpoints[$this->CurrCol][] = $this->y;\n        } // *COLUMNS*\n\n        if ($split) {\n            // Are there more columns to print on a next page?\n            if ($lastCol < $numcols - 1) {\n                $splitpg++;\n                $startcol = $lastCol + 1;\n                return [false, $startrow, $startcol, $splitpg, $returny, $y0];\n            } else {\n                return [true, 0, 0, 0, false, false];\n            }\n        }\n    }\n\n    // END OF FUNCTION _tableWrite()\n    /////////////////////////END OF TABLE CODE//////////////////////////////////\n    /* -- END TABLES -- */\n\n    function _putextgstates()\n    {\n        for ($i = 1; $i <= count($this->extgstates); $i++) {\n            $this->writer->object();\n            $this->extgstates[$i]['n'] = $this->n;\n            $this->writer->write('<</Type /ExtGState');\n            foreach ($this->extgstates[$i]['parms'] as $k => $v) {\n                $this->writer->write('/' . $k . ' ' . $v);\n            }\n            $this->writer->write('>>');\n            $this->writer->write('endobj');\n        }\n    }\n\n    function SetProtection($permissions = [], $user_pass = '', $owner_pass = null, $length = 40)\n    {\n        $this->encrypted = $this->protection->setProtection($permissions, $user_pass, $owner_pass, $length);\n    }\n\n    // =========================================\n    // FROM class PDF_Bookmark\n    function Bookmark($txt, $level = 0, $y = 0)\n    {\n        $txt = $this->purify_utf8_text($txt);\n        if ($this->text_input_as_HTML) {\n            $txt = $this->all_entities_to_utf8($txt);\n        }\n        if ($y == -1) {\n            if (!$this->ColActive) {\n                $y = $this->y;\n            } else {\n                $y = $this->y0;\n            } // If columns are on - mark top of columns\n        }\n\n        // else y is used as set, or =0 i.e. top of page\n        // DIRECTIONALITY RTL\n        $bmo = ['t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->page];\n\n        if ($this->keep_block_together) {\n            // do nothing\n        } elseif ($this->table_rotate) {\n            $this->tbrot_BMoutlines[] = $bmo;\n        } elseif ($this->kwt) {\n            $this->kwt_BMoutlines[] = $bmo;\n        } elseif ($this->ColActive) {\n            $this->col_BMoutlines[] = $bmo;\n        } else {\n            $this->BMoutlines[] = $bmo;\n        }\n    }\n\n    /**\n     * Initiate, and Mark a place for the Table of Contents to be inserted\n     */\n    function TOC(\n        $tocfont = '',\n        $tocfontsize = 0,\n        $tocindent = 0,\n        $resetpagenum = '',\n        $pagenumstyle = '',\n        $suppress = '',\n        $toc_orientation = '',\n        $TOCusePaging = true,\n        $TOCuseLinking = false,\n        $toc_id = 0,\n        $tocoutdent = ''\n    ) {\n\n        $this->tableOfContents->TOC(\n            $tocfont,\n            $tocfontsize,\n            $tocindent,\n            $resetpagenum,\n            $pagenumstyle,\n            $suppress,\n            $toc_orientation,\n            $TOCusePaging,\n            $TOCuseLinking,\n            $toc_id,\n            $tocoutdent\n        );\n    }\n\n    function TOCpagebreakByArray($a)\n    {\n        if (!is_array($a)) {\n            $a = [];\n        }\n        $tocoutdent = (isset($a['tocoutdent']) ? $a['tocoutdent'] : (isset($a['outdent']) ? $a['outdent'] : ''));\n        $TOCusePaging = (isset($a['TOCusePaging']) ? $a['TOCusePaging'] : (isset($a['paging']) ? $a['paging'] : true));\n        $TOCuseLinking = (isset($a['TOCuseLinking']) ? $a['TOCuseLinking'] : (isset($a['links']) ? $a['links'] : ''));\n        $toc_orientation = (isset($a['toc_orientation']) ? $a['toc_orientation'] : (isset($a['toc-orientation']) ? $a['toc-orientation'] : ''));\n        $toc_mgl = (isset($a['toc_mgl']) ? $a['toc_mgl'] : (isset($a['toc-margin-left']) ? $a['toc-margin-left'] : ''));\n        $toc_mgr = (isset($a['toc_mgr']) ? $a['toc_mgr'] : (isset($a['toc-margin-right']) ? $a['toc-margin-right'] : ''));\n        $toc_mgt = (isset($a['toc_mgt']) ? $a['toc_mgt'] : (isset($a['toc-margin-top']) ? $a['toc-margin-top'] : ''));\n        $toc_mgb = (isset($a['toc_mgb']) ? $a['toc_mgb'] : (isset($a['toc-margin-bottom']) ? $a['toc-margin-bottom'] : ''));\n        $toc_mgh = (isset($a['toc_mgh']) ? $a['toc_mgh'] : (isset($a['toc-margin-header']) ? $a['toc-margin-header'] : ''));\n        $toc_mgf = (isset($a['toc_mgf']) ? $a['toc_mgf'] : (isset($a['toc-margin-footer']) ? $a['toc-margin-footer'] : ''));\n        $toc_ohname = (isset($a['toc_ohname']) ? $a['toc_ohname'] : (isset($a['toc-odd-header-name']) ? $a['toc-odd-header-name'] : ''));\n        $toc_ehname = (isset($a['toc_ehname']) ? $a['toc_ehname'] : (isset($a['toc-even-header-name']) ? $a['toc-even-header-name'] : ''));\n        $toc_ofname = (isset($a['toc_ofname']) ? $a['toc_ofname'] : (isset($a['toc-odd-footer-name']) ? $a['toc-odd-footer-name'] : ''));\n        $toc_efname = (isset($a['toc_efname']) ? $a['toc_efname'] : (isset($a['toc-even-footer-name']) ? $a['toc-even-footer-name'] : ''));\n        $toc_ohvalue = (isset($a['toc_ohvalue']) ? $a['toc_ohvalue'] : (isset($a['toc-odd-header-value']) ? $a['toc-odd-header-value'] : 0));\n        $toc_ehvalue = (isset($a['toc_ehvalue']) ? $a['toc_ehvalue'] : (isset($a['toc-even-header-value']) ? $a['toc-even-header-value'] : 0));\n        $toc_ofvalue = (isset($a['toc_ofvalue']) ? $a['toc_ofvalue'] : (isset($a['toc-odd-footer-value']) ? $a['toc-odd-footer-value'] : 0));\n        $toc_efvalue = (isset($a['toc_efvalue']) ? $a['toc_efvalue'] : (isset($a['toc-even-footer-value']) ? $a['toc-even-footer-value'] : 0));\n        $toc_preHTML = (isset($a['toc_preHTML']) ? $a['toc_preHTML'] : (isset($a['toc-preHTML']) ? $a['toc-preHTML'] : ''));\n        $toc_postHTML = (isset($a['toc_postHTML']) ? $a['toc_postHTML'] : (isset($a['toc-postHTML']) ? $a['toc-postHTML'] : ''));\n        $toc_bookmarkText = (isset($a['toc_bookmarkText']) ? $a['toc_bookmarkText'] : (isset($a['toc-bookmarkText']) ? $a['toc-bookmarkText'] : ''));\n        $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');\n        $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');\n        $suppress = (isset($a['suppress']) ? $a['suppress'] : '');\n        $orientation = (isset($a['orientation']) ? $a['orientation'] : '');\n        $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));\n        $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));\n        $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));\n        $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));\n        $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));\n        $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));\n        $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));\n        $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));\n        $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));\n        $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));\n        $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));\n        $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));\n        $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));\n        $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));\n        $toc_id = (isset($a['toc_id']) ? $a['toc_id'] : (isset($a['name']) ? $a['name'] : 0));\n        $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));\n        $toc_pagesel = (isset($a['toc_pagesel']) ? $a['toc_pagesel'] : (isset($a['toc-pageselector']) ? $a['toc-pageselector'] : ''));\n        $sheetsize = (isset($a['sheetsize']) ? $a['sheetsize'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));\n        $toc_sheetsize = (isset($a['toc_sheetsize']) ? $a['toc_sheetsize'] : (isset($a['toc-sheet-size']) ? $a['toc-sheet-size'] : ''));\n\n        $this->TOCpagebreak('', '', '', $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent);\n    }\n\n    function TOCpagebreak($tocfont = '', $tocfontsize = '', $tocindent = '', $TOCusePaging = true, $TOCuseLinking = '', $toc_orientation = '', $toc_mgl = '', $toc_mgr = '', $toc_mgt = '', $toc_mgb = '', $toc_mgh = '', $toc_mgf = '', $toc_ohname = '', $toc_ehname = '', $toc_ofname = '', $toc_efname = '', $toc_ohvalue = 0, $toc_ehvalue = 0, $toc_ofvalue = 0, $toc_efvalue = 0, $toc_preHTML = '', $toc_postHTML = '', $toc_bookmarkText = '', $resetpagenum = '', $pagenumstyle = '', $suppress = '', $orientation = '', $mgl = '', $mgr = '', $mgt = '', $mgb = '', $mgh = '', $mgf = '', $ohname = '', $ehname = '', $ofname = '', $efname = '', $ohvalue = 0, $ehvalue = 0, $ofvalue = 0, $efvalue = 0, $toc_id = 0, $pagesel = '', $toc_pagesel = '', $sheetsize = '', $toc_sheetsize = '', $tocoutdent = '')\n    {\n        // Start a new page\n        if ($this->state == 0) {\n            $this->AddPage();\n        }\n        if ($this->y == $this->tMargin && (!$this->mirrorMargins || ($this->mirrorMargins && $this->page % 2 == 1))) {\n            // Don't add a page\n            if ($this->page == 1 && count($this->PageNumSubstitutions) == 0) {\n                if (!$suppress) {\n                    $suppress = 'off';\n                }\n                // $this->PageNumSubstitutions[] = array('from'=>1, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=> $suppress);\n            }\n            $this->PageNumSubstitutions[] = ['from' => $this->page, 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress];\n        } else {\n            $this->AddPage($orientation, 'NEXT-ODD', $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $sheetsize);\n        }\n        $this->tableOfContents->TOCpagebreak($tocfont, $tocfontsize, $tocindent, $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent);\n    }\n\n    function TOC_Entry($txt, $level = 0, $toc_id = 0)\n    {\n        if ($this->ColActive) {\n            $ily = $this->y0;\n        } else {\n            $ily = $this->y;\n        } // use top of columns\n\n        $linkn = $this->AddLink();\n        $uid = '__mpdfinternallink_' . $linkn;\n        if ($this->table_rotate) {\n            $this->internallink[$uid] = [\"Y\" => $ily, \"PAGE\" => $this->page, \"tbrot\" => true];\n        } elseif ($this->kwt) {\n            $this->internallink[$uid] = [\"Y\" => $ily, \"PAGE\" => $this->page, \"kwt\" => true];\n        } elseif ($this->ColActive) {\n            $this->internallink[$uid] = [\"Y\" => $ily, \"PAGE\" => $this->page, \"col\" => $this->CurrCol];\n        } elseif (!$this->keep_block_together) {\n            $this->internallink[$uid] = [\"Y\" => $ily, \"PAGE\" => $this->page];\n        }\n        $this->internallink['#' . $uid] = $linkn;\n        $this->SetLink($linkn, $ily, $this->page);\n\n        if (strtoupper($toc_id) == 'ALL') {\n            $toc_id = '_mpdf_all';\n        } elseif (!$toc_id) {\n            $toc_id = 0;\n        } else {\n            $toc_id = strtolower($toc_id);\n        }\n        $btoc = ['t' => $txt, 'l' => $level, 'p' => $this->page, 'link' => $linkn, 'toc_id' => $toc_id];\n        if ($this->keep_block_together) {\n            // do nothing\n        } /* -- TABLES -- */ elseif ($this->table_rotate) {\n            $this->tbrot_toc[] = $btoc;\n        } elseif ($this->kwt) {\n            $this->kwt_toc[] = $btoc;\n        } /* -- END TABLES -- */ elseif ($this->ColActive) {  // *COLUMNS*\n            $this->col_toc[] = $btoc; // *COLUMNS*\n        } // *COLUMNS*\n        else {\n            $this->tableOfContents->_toc[] = $btoc;\n        }\n    }\n\n    /* -- END TOC -- */\n\n    // ======================================================\n    function MovePages($target_page, $start_page, $end_page = -1)\n    {\n        // move a page/pages EARLIER in the document\n        if ($end_page < 1) {\n            $end_page = $start_page;\n        }\n        $n_toc = $end_page - $start_page + 1;\n\n        // Set/Update PageNumSubstitutions changes before moving anything\n        if (count($this->PageNumSubstitutions)) {\n            $tp_present = false;\n            $sp_present = false;\n            $ep_present = false;\n            foreach ($this->PageNumSubstitutions as $k => $v) {\n                if ($this->PageNumSubstitutions[$k]['from'] == $target_page) {\n                    $tp_present = true;\n                    if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {\n                        $this->PageNumSubstitutions[$k]['suppress'] = 'off';\n                    }\n                }\n                if ($this->PageNumSubstitutions[$k]['from'] == $start_page) {\n                    $sp_present = true;\n                    if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {\n                        $this->PageNumSubstitutions[$k]['suppress'] = 'off';\n                    }\n                }\n                if ($this->PageNumSubstitutions[$k]['from'] == ($end_page + 1)) {\n                    $ep_present = true;\n                    if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {\n                        $this->PageNumSubstitutions[$k]['suppress'] = 'off';\n                    }\n                }\n            }\n\n            if (!$tp_present) {\n                [$tp_type, $tp_suppress, $tp_reset] = $this->docPageSettings($target_page);\n            }\n            if (!$sp_present) {\n                [$sp_type, $sp_suppress, $sp_reset] = $this->docPageSettings($start_page);\n            }\n            if (!$ep_present) {\n                [$ep_type, $ep_suppress, $ep_reset] = $this->docPageSettings($start_page - 1);\n            }\n        }\n\n        $last = [];\n        // store pages\n        for ($i = $start_page; $i <= $end_page; $i++) {\n            $last[] = $this->pages[$i];\n        }\n        // move pages\n        for ($i = $start_page - 1; $i >= ($target_page); $i--) {\n            $this->pages[$i + $n_toc] = $this->pages[$i];\n        }\n        // Put toc pages at insert point\n        for ($i = 0; $i < $n_toc; $i++) {\n            $this->pages[$target_page + $i] = $last[$i];\n        }\n\n        /* -- BOOKMARKS -- */\n        // Update Bookmarks\n        foreach ($this->BMoutlines as $i => $o) {\n            if ($o['p'] >= $target_page) {\n                $this->BMoutlines[$i]['p'] += $n_toc;\n            }\n        }\n        /* -- END BOOKMARKS -- */\n\n        // Update Page Links\n        if (count($this->PageLinks)) {\n            $newarr = [];\n            foreach ($this->PageLinks as $i => $o) {\n                foreach ($this->PageLinks[$i] as $key => $pl) {\n                    if (strpos($pl[4], '@') === 0) {\n                        $p = substr($pl[4], 1);\n                        if ($p >= $start_page && $p <= $end_page) {\n                            $this->PageLinks[$i][$key][4] = '@' . ($p + ($target_page - $start_page));\n                        } elseif ($p >= $target_page && $p < $start_page) {\n                            $this->PageLinks[$i][$key][4] = '@' . ($p + $n_toc);\n                        }\n                    }\n                }\n                if ($i >= $start_page && $i <= $end_page) {\n                    $newarr[($i + ($target_page - $start_page))] = $this->PageLinks[$i];\n                } elseif ($i >= $target_page && $i < $start_page) {\n                    $newarr[($i + $n_toc)] = $this->PageLinks[$i];\n                } else {\n                    $newarr[$i] = $this->PageLinks[$i];\n                }\n            }\n            $this->PageLinks = $newarr;\n        }\n\n        // OrientationChanges\n        if (count($this->OrientationChanges)) {\n            $newarr = [];\n            foreach ($this->OrientationChanges as $p => $v) {\n                if ($p >= $start_page && $p <= $end_page) {\n                    $newarr[($p + ($target_page - $start_page))] = $this->OrientationChanges[$p];\n                } elseif ($p >= $target_page && $p < $start_page) {\n                    $newarr[$p + $n_toc] = $this->OrientationChanges[$p];\n                } else {\n                    $newarr[$p] = $this->OrientationChanges[$p];\n                }\n            }\n            ksort($newarr);\n            $this->OrientationChanges = $newarr;\n        }\n\n        // Page Dimensions\n        if (count($this->pageDim)) {\n            $newarr = [];\n            foreach ($this->pageDim as $p => $v) {\n                if ($p >= $start_page && $p <= $end_page) {\n                    $newarr[($p + ($target_page - $start_page))] = $this->pageDim[$p];\n                } elseif ($p >= $target_page && $p < $start_page) {\n                    $newarr[$p + $n_toc] = $this->pageDim[$p];\n                } else {\n                    $newarr[$p] = $this->pageDim[$p];\n                }\n            }\n            ksort($newarr);\n            $this->pageDim = $newarr;\n        }\n\n        // HTML Headers & Footers\n        if (count($this->saveHTMLHeader)) {\n            $newarr = [];\n            foreach ($this->saveHTMLHeader as $p => $v) {\n                if ($p >= $start_page && $p <= $end_page) {\n                    $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLHeader[$p];\n                } elseif ($p >= $target_page && $p < $start_page) {\n                    $newarr[$p + $n_toc] = $this->saveHTMLHeader[$p];\n                } else {\n                    $newarr[$p] = $this->saveHTMLHeader[$p];\n                }\n            }\n            ksort($newarr);\n            $this->saveHTMLHeader = $newarr;\n        }\n        if (count($this->saveHTMLFooter)) {\n            $newarr = [];\n            foreach ($this->saveHTMLFooter as $p => $v) {\n                if ($p >= $start_page && $p <= $end_page) {\n                    $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLFooter[$p];\n                } elseif ($p >= $target_page && $p < $start_page) {\n                    $newarr[$p + $n_toc] = $this->saveHTMLFooter[$p];\n                } else {\n                    $newarr[$p] = $this->saveHTMLFooter[$p];\n                }\n            }\n            ksort($newarr);\n            $this->saveHTMLFooter = $newarr;\n        }\n\n        // Update Internal Links\n        if (count($this->internallink)) {\n            foreach ($this->internallink as $key => $o) {\n                if (is_array($o) && $o['PAGE'] >= $start_page && $o['PAGE'] <= $end_page) {\n                    $this->internallink[$key]['PAGE'] += ($target_page - $start_page);\n                } elseif (is_array($o) && $o['PAGE'] >= $target_page && $o['PAGE'] < $start_page) {\n                    $this->internallink[$key]['PAGE'] += $n_toc;\n                }\n            }\n        }\n\n        // Update Links\n        if (count($this->links)) {\n            foreach ($this->links as $key => $o) {\n                if ($o[0] >= $start_page && $o[0] <= $end_page) {\n                    $this->links[$key][0] += ($target_page - $start_page);\n                }\n                if ($o[0] >= $target_page && $o[0] < $start_page) {\n                    $this->links[$key][0] += $n_toc;\n                }\n            }\n        }\n\n        // Update Form fields\n        if (count($this->form->forms)) {\n            foreach ($this->form->forms as $key => $f) {\n                if ($f['page'] >= $start_page && $f['page'] <= $end_page) {\n                    $this->form->forms[$key]['page'] += ($target_page - $start_page);\n                }\n                if ($f['page'] >= $target_page && $f['page'] < $start_page) {\n                    $this->form->forms[$key]['page'] += $n_toc;\n                }\n            }\n        }\n\n        /* -- ANNOTATIONS -- */\n        // Update Annotations\n        if (count($this->PageAnnots)) {\n            $newarr = [];\n            foreach ($this->PageAnnots as $p => $anno) {\n                if ($p >= $start_page && $p <= $end_page) {\n                    $np = $p + ($target_page - $start_page);\n                    foreach ($anno as $o) {\n                        $newarr[$np][] = $o;\n                    }\n                } elseif ($p >= $target_page && $p < $start_page) {\n                    $np = $p + $n_toc;\n                    foreach ($anno as $o) {\n                        $newarr[$np][] = $o;\n                    }\n                } else {\n                    $newarr[$p] = $this->PageAnnots[$p];\n                }\n            }\n            $this->PageAnnots = $newarr;\n            unset($newarr);\n        }\n        /* -- END ANNOTATIONS -- */\n\n        // Update TOC pages\n        if (count($this->tableOfContents->_toc)) {\n            foreach ($this->tableOfContents->_toc as $key => $t) {\n                if ($t['p'] >= $start_page && $t['p'] <= $end_page) {\n                    $this->tableOfContents->_toc[$key]['p'] += ($target_page - $start_page);\n                }\n                if ($t['p'] >= $target_page && $t['p'] < $start_page) {\n                    $this->tableOfContents->_toc[$key]['p'] += $n_toc;\n                }\n            }\n        }\n\n        // Update PageNumSubstitutions\n        if (count($this->PageNumSubstitutions)) {\n            $newarr = [];\n            foreach ($this->PageNumSubstitutions as $k => $v) {\n                if ($this->PageNumSubstitutions[$k]['from'] >= $start_page && $this->PageNumSubstitutions[$k]['from'] <= $end_page) {\n                    $this->PageNumSubstitutions[$k]['from'] += ($target_page - $start_page);\n                    $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];\n                } elseif ($this->PageNumSubstitutions[$k]['from'] >= $target_page && $this->PageNumSubstitutions[$k]['from'] < $start_page) {\n                    $this->PageNumSubstitutions[$k]['from'] += $n_toc;\n                    $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];\n                } else {\n                    $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];\n                }\n            }\n\n            if (!$sp_present) {\n                $newarr[$target_page] = ['from' => $target_page, 'suppress' => $sp_suppress, 'reset' => $sp_reset, 'type' => $sp_type];\n            }\n            if (!$tp_present) {\n                $newarr[($target_page + $n_toc)] = ['from' => ($target_page + $n_toc), 'suppress' => $tp_suppress, 'reset' => $tp_reset, 'type' => $tp_type];\n            }\n            if (!$ep_present && $end_page > count($this->pages)) {\n                $newarr[($end_page + 1)] = ['from' => ($end_page + 1), 'suppress' => $ep_suppress, 'reset' => $ep_reset, 'type' => $ep_type];\n            }\n            ksort($newarr);\n            $this->PageNumSubstitutions = [];\n            foreach ($newarr as $v) {\n                $this->PageNumSubstitutions[] = $v;\n            }\n        }\n    }\n\n    function DeletePages($start_page, $end_page = -1)\n    {\n        // move a page/pages EARLIER in the document\n        if ($end_page < 1) {\n            $end_page = $start_page;\n        }\n        $n_tod = $end_page - $start_page + 1;\n        $last_page = count($this->pages);\n        $n_atend = $last_page - $end_page + 1;\n\n        // move pages\n        for ($i = 0; $i < $n_atend; $i++) {\n            $this->pages[$start_page + $i] = $this->pages[$end_page + 1 + $i];\n        }\n        // delete pages\n        for ($i = 0; $i < $n_tod; $i++) {\n            unset($this->pages[$last_page - $i]);\n        }\n\n\n        /* -- BOOKMARKS -- */\n        // Update Bookmarks\n        foreach ($this->BMoutlines as $i => $o) {\n            if ($o['p'] >= $end_page) {\n                $this->BMoutlines[$i]['p'] -= $n_tod;\n            } elseif ($p < $start_page) {\n                unset($this->BMoutlines[$i]);\n            }\n        }\n        /* -- END BOOKMARKS -- */\n\n        // Update Page Links\n        if (count($this->PageLinks)) {\n            $newarr = [];\n            foreach ($this->PageLinks as $i => $o) {\n                foreach ($this->PageLinks[$i] as $key => $pl) {\n                    if (strpos($pl[4], '@') === 0) {\n                        $p = substr($pl[4], 1);\n                        if ($p > $end_page) {\n                            $this->PageLinks[$i][$key][4] = '@' . ($p - $n_tod);\n                        } elseif ($p < $start_page) {\n                            unset($this->PageLinks[$i][$key]);\n                        }\n                    }\n                }\n                if ($i > $end_page) {\n                    $newarr[($i - $n_tod)] = $this->PageLinks[$i];\n                } elseif ($p < $start_page) {\n                    $newarr[$i] = $this->PageLinks[$i];\n                }\n            }\n            $this->PageLinks = $newarr;\n        }\n\n        // OrientationChanges\n        if (count($this->OrientationChanges)) {\n            $newarr = [];\n            foreach ($this->OrientationChanges as $p => $v) {\n                if ($p > $end_page) {\n                    $newarr[($p - $t_tod)] = $this->OrientationChanges[$p];\n                } elseif ($p < $start_page) {\n                    $newarr[$p] = $this->OrientationChanges[$p];\n                }\n            }\n            ksort($newarr);\n            $this->OrientationChanges = $newarr;\n        }\n\n        // Page Dimensions\n        if (count($this->pageDim)) {\n            $newarr = [];\n            foreach ($this->pageDim as $p => $v) {\n                if ($p > $end_page) {\n                    $newarr[($p - $n_tod)] = $this->pageDim[$p];\n                } elseif ($p < $start_page) {\n                    $newarr[$p] = $this->pageDim[$p];\n                }\n            }\n            ksort($newarr);\n            $this->pageDim = $newarr;\n        }\n\n        // HTML Headers & Footers\n        if (count($this->saveHTMLHeader)) {\n            foreach ($this->saveHTMLHeader as $p => $v) {\n                if ($p > $end_page) {\n                    $newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p];\n                } // mPDF 5.7.3\n                elseif ($p < $start_page) {\n                    $newarr[$p] = $this->saveHTMLHeader[$p];\n                }\n            }\n            ksort($newarr);\n            $this->saveHTMLHeader = $newarr;\n        }\n        if (count($this->saveHTMLFooter)) {\n            $newarr = [];\n            foreach ($this->saveHTMLFooter as $p => $v) {\n                if ($p > $end_page) {\n                    $newarr[($p - $n_tod)] = $this->saveHTMLFooter[$p];\n                } elseif ($p < $start_page) {\n                    $newarr[$p] = $this->saveHTMLFooter[$p];\n                }\n            }\n            ksort($newarr);\n            $this->saveHTMLFooter = $newarr;\n        }\n\n        // Update Internal Links\n        foreach ($this->internallink as $key => $o) {\n            if ($o['PAGE'] > $end_page) {\n                $this->internallink[$key]['PAGE'] -= $n_tod;\n            } elseif ($o['PAGE'] < $start_page) {\n                unset($this->internallink[$key]);\n            }\n        }\n\n        // Update Links\n        foreach ($this->links as $key => $o) {\n            if ($o[0] > $end_page) {\n                $this->links[$key][0] -= $n_tod;\n            } elseif ($o[0] < $start_page) {\n                unset($this->links[$key]);\n            }\n        }\n\n        // Update Form fields\n        foreach ($this->form->forms as $key => $f) {\n            if ($f['page'] > $end_page) {\n                $this->form->forms[$key]['page'] -= $n_tod;\n            } elseif ($f['page'] < $start_page) {\n                unset($this->form->forms[$key]);\n            }\n        }\n\n        /* -- ANNOTATIONS -- */\n        // Update Annotations\n        if (count($this->PageAnnots)) {\n            $newarr = [];\n            foreach ($this->PageAnnots as $p => $anno) {\n                if ($p > $end_page) {\n                    foreach ($anno as $o) {\n                        $newarr[($p - $n_tod)][] = $o;\n                    }\n                } elseif ($p < $start_page) {\n                    $newarr[$p] = $this->PageAnnots[$p];\n                }\n            }\n            ksort($newarr);\n            $this->PageAnnots = $newarr;\n        }\n        /* -- END ANNOTATIONS -- */\n\n        // Update PageNumSubstitutions\n        foreach ($this->PageNumSubstitutions as $k => $v) {\n            if ($this->PageNumSubstitutions[$k]['from'] > $end_page) {\n                $this->PageNumSubstitutions[$k]['from'] -= $n_tod;\n            } elseif ($this->PageNumSubstitutions[$k]['from'] < $start_page) {\n                unset($this->PageNumSubstitutions[$k]);\n            }\n        }\n\n        unset($newarr);\n        $this->page = count($this->pages);\n    }\n\n    // ======================================================\n    /* -- INDEX -- */\n    // FROM class PDF_Ref == INDEX\n\n    function IndexEntry($txt, $xref = '')\n    {\n        if ($xref) {\n            $this->IndexEntrySee($txt, $xref);\n            return;\n        }\n\n        // Search the reference (AND Ref/PageNo) in the array\n        $Present = false;\n        if ($this->keep_block_together) {\n            // do nothing\n        } /* -- TABLES -- */ elseif ($this->kwt) {\n            $size = count($this->kwt_Reference);\n            for ($i = 0; $i < $size; $i++) {\n                if (isset($this->kwt_Reference[$i]['t']) && $this->kwt_Reference[$i]['t'] == $txt) {\n                    $Present = true;\n                    if ($this->page != $this->kwt_Reference[$i]['op']) {\n                        $this->kwt_Reference[$i]['op'] = $this->page;\n                    }\n                }\n            }\n            if (!$Present) { // If not found, add it\n                $this->kwt_Reference[] = ['t' => $txt, 'op' => $this->page];\n            }\n        } /* -- END TABLES -- */ else {\n            $size = count($this->Reference);\n            for ($i = 0; $i < $size; $i++) {\n                if (isset($this->Reference[$i]['t']) && $this->Reference[$i]['t'] == $txt) {\n                    $Present = true;\n                    if (!in_array($this->page, $this->Reference[$i]['p'])) {\n                        $this->Reference[$i]['p'][] = $this->page;\n                    }\n                }\n            }\n            if (!$Present) { // If not found, add it\n                $this->Reference[] = ['t' => $txt, 'p' => [$this->page]];\n            }\n        }\n    }\n\n    // Added function to add a reference \"Elephants. See Chickens\"\n    function IndexEntrySee($txta, $txtb)\n    {\n        if ($this->directionality == 'rtl') { // *OTL*\n            // ONLY DO THIS IF NOT IN TAGS\n            if ($txta == strip_tags($txta)) {\n                $txta = str_replace(':', ' - ', $txta); // *OTL*\n            }\n            if ($txtb == strip_tags($txtb)) {\n                $txtb = str_replace(':', ' - ', $txtb); // *OTL*\n            }\n        } // *OTL*\n        else { // *OTL*\n            if ($txta == strip_tags($txta)) {\n                $txta = str_replace(':', ', ', $txta);\n            }\n            if ($txtb == strip_tags($txtb)) {\n                $txtb = str_replace(':', ', ', $txtb);\n            }\n        } // *OTL*\n        $this->Reference[] = ['t' => $txta . ' - see ' . $txtb, 'p' => []];\n    }\n\n    private function filesInDir($directory)\n    {\n        $files = [];\n        foreach ((new \\DirectoryIterator($directory)) as $v) {\n            if ($v->isDir() || $v->isDot()) {\n                continue;\n            }\n\n            $files[] = $v->getPathname();\n        }\n\n        return $files;\n    }\n\n    function InsertIndex($usedivletters = 1, $useLinking = false, $indexCollationLocale = '', $indexCollationGroup = '')\n    {\n        $size = count($this->Reference);\n        if ($size == 0) {\n            return false;\n        }\n\n        // $spacer used after named entry\n        // $sep  separates number [groups], $joiner joins numbers in range\n        //  e.g. \"elephant 73, 97-99\"  =  elephant[$spacer]73[$sep]97[$joiner]99\n        // $subEntrySeparator separates main and subentry (if $this->indexUseSubentries == false;) e.g.\n        // Mammal:elephant => Mammal[$subEntrySeparator]elephant\n        // $subEntryInset specifies what precedes a subentry (if $this->indexUseSubentries == true;) e.g.\n        // Mammal:elephant => [$subEntryInset]elephant\n        $spacer = \"\\xc2\\xa0 \";\n        if ($this->directionality == 'rtl') {\n            $sep = '&#x060c; ';\n            $joiner = '-';\n            $subEntrySeparator = '&#x060c; ';\n            $subEntryInset = ' - ';\n        } else {\n            $sep = ', ';\n            $joiner = '-';\n            $subEntrySeparator = ', ';\n            $subEntryInset = ' - ';\n        }\n\n        for ($i = 0; $i < $size; $i++) {\n            $txt = $this->Reference[$i]['t'];\n            $txt = strip_tags($txt); // mPDF 6\n            $txt = $this->purify_utf8($txt);\n            $this->Reference[$i]['uf'] = $txt; // Unformatted e.g. pure utf-8 encoded characters, no mark-up/tags\n            // Used for ordering and collation\n        }\n\n        if ($usedivletters) {\n            if ($indexCollationGroup && \\in_array(strtolower($indexCollationGroup), array_map(function ($v) {\n                    return strtolower(basename($v, '.php'));\n                }, $this->filesInDir(__DIR__ . '/../data/collations/')))) {\n                $collation = require __DIR__ . '/../data/collations/' . $indexCollationGroup . '.php';\n            } else {\n                $collation = [];\n            }\n            for ($i = 0; $i < $size; $i++) {\n                if ($this->Reference[$i]['uf']) {\n                    $l = mb_substr($this->Reference[$i]['uf'], 0, 1, 'UTF-8');\n                    if (isset($indexCollationGroup) && $indexCollationGroup) {\n                        $uni = $this->UTF8StringToArray($l);\n                        $ucode = $uni[0];\n                        if (isset($collation[$ucode])) {\n                            $this->Reference[$i]['d'] = UtfString::code2utf($collation[$ucode]);\n                        } else {\n                            $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');\n                        }\n                    } else {\n                        $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');\n                    }\n                }\n            }\n        }\n\n        // Alphabetic sort of the references\n        $originalLocale = setlocale(LC_COLLATE, 0);\n        if ($indexCollationLocale) {\n            setlocale(LC_COLLATE, $indexCollationLocale);\n        }\n\n        usort($this->Reference, function ($a, $b) {\n            return strcoll(strtolower($a['uf']), strtolower($b['uf']));\n        });\n\n        if ($indexCollationLocale) {\n            setlocale(LC_COLLATE, $originalLocale);\n        }\n\n        $html = '<div class=\"mpdf_index_main\">';\n\n        $lett = '';\n        $last_lett = '';\n        $mainentry = '';\n        for ($i = 0; $i < $size; $i++) {\n            if ($this->Reference[$i]['t']) {\n                if ($usedivletters) {\n                    $lett = $this->Reference[$i]['d'];\n                    if ($lett != $last_lett) {\n                        $html .= '<div class=\"mpdf_index_letter\">' . $lett . '</div>';\n                    }\n                }\n                $txt = $this->Reference[$i]['t'];\n\n                // Sub-entries e.g. Mammals:elephant\n                // But allow for tags e.g. <b>Mammal</b>:elephants\n                $a = preg_split('/(<.*?>)/', $txt, -1, PREG_SPLIT_DELIM_CAPTURE);\n                $txt = '';\n                $marker = false;\n                foreach ($a as $k => $e) {\n                    if ($k % 2 == 0 && !$marker) {\n                        if (strpos($e, ':') !== false) { // == SubEntry\n                            if ($this->indexUseSubentries) {\n                                // If the Main entry does not have any page numbers associated with it\n                                // create and insert an entry\n                                [$txtmain, $sub] = preg_split('/[:]/', $e, 2);\n                                if (strip_tags($txt . $txtmain) != $mainentry) {\n                                    $html .= '<div class=\"mpdf_index_entry\">' . $txt . $txtmain . '</div>';\n                                    $mainentry = strip_tags($txt . $txtmain);\n                                }\n\n                                $txt = $subEntryInset;\n                                $e = $sub; // Only replace first one\n                            } else {\n                                $e = preg_replace('/[:]/', $subEntrySeparator, $e, 1); // Only replace first one\n                            }\n                            $marker = true; // Don't replace any more once the subentry marker has been found\n                        }\n                    }\n                    $txt .= $e;\n                }\n\n                if (!$marker) {\n                    $mainentry = strip_tags($txt);\n                }\n\n                $html .= '<div class=\"mpdf_index_entry\">';\n                $html .= $txt;\n                $ppp = $this->Reference[$i]['p']; // = array of page numbers to point to\n                if (count($ppp)) {\n                    sort($ppp);\n                    $newarr = [];\n                    $range_start = $ppp[0];\n                    $range_end = 0;\n\n                    $html .= $spacer;\n\n                    for ($zi = 1; $zi < count($ppp); $zi++) {\n                        if ($ppp[$zi] == ($ppp[($zi - 1)] + 1)) {\n                            $range_end = $ppp[$zi];\n                        } else {\n                            if ($range_end) {\n                                if ($range_end == $range_start + 1) {\n                                    if ($useLinking) {\n                                        $html .= '<a class=\"mpdf_index_link\" href=\"@' . $range_start . '\">';\n                                    }\n                                    $html .= $this->docPageNum($range_start);\n                                    if ($useLinking) {\n                                        $html .= '</a>';\n                                    }\n                                    $html .= $sep;\n\n                                    if ($useLinking) {\n                                        $html .= '<a class=\"mpdf_index_link\" href=\"@' . $ppp[$zi - 1] . '\">';\n                                    }\n                                    $html .= $this->docPageNum($ppp[$zi - 1]);\n                                    if ($useLinking) {\n                                        $html .= '</a>';\n                                    }\n                                    $html .= $sep;\n                                }\n                            } else {\n                                if ($useLinking) {\n                                    $html .= '<a class=\"mpdf_index_link\" href=\"@' . $ppp[$zi - 1] . '\">';\n                                }\n                                $html .= $this->docPageNum($ppp[$zi - 1]);\n                                if ($useLinking) {\n                                    $html .= '</a>';\n                                }\n                                $html .= $sep;\n                            }\n                            $range_start = $ppp[$zi];\n                            $range_end = 0;\n                        }\n                    }\n\n                    if ($range_end) {\n                        if ($useLinking) {\n                            $html .= '<a class=\"mpdf_index_link\" href=\"@' . $range_start . '\">';\n                        }\n                        $html .= $this->docPageNum($range_start);\n                        if ($range_end == $range_start + 1) {\n                            if ($useLinking) {\n                                $html .= '</a>';\n                            }\n                            $html .= $sep;\n                            if ($useLinking) {\n                                $html .= '<a class=\"mpdf_index_link\" href=\"@' . $range_end . '\">';\n                            }\n                            $html .= $this->docPageNum($range_end);\n                            if ($useLinking) {\n                                $html .= '</a>';\n                            }\n                        } else {\n                            $html .= $joiner;\n                            $html .= $this->docPageNum($range_end);\n                            if ($useLinking) {\n                                $html .= '</a>';\n                            }\n                        }\n                    } else {\n                        if ($useLinking) {\n                            $html .= '<a class=\"mpdf_index_link\" href=\"@' . $ppp[(count($ppp) - 1)] . '\">';\n                        }\n                        $html .= $this->docPageNum($ppp[(count($ppp) - 1)]);\n                        if ($useLinking) {\n                            $html .= '</a>';\n                        }\n                    }\n                }\n            }\n            $html .= '</div>';\n            $last_lett = $lett;\n        }\n        $html .= '</div>';\n        $save_fpb = $this->fixedPosBlockSave;\n        $this->WriteHTML($html);\n        $this->fixedPosBlockSave = $save_fpb;\n\n        $this->breakpoints[$this->CurrCol][] = $this->y;  // *COLUMNS*\n    }\n\n    /* -- END INDEX -- */\n\n    function AcceptPageBreak()\n    {\n        if (count($this->cellBorderBuffer)) {\n            $this->printcellbuffer();\n        } // *TABLES*\n        /* -- COLUMNS -- */\n        if ($this->ColActive == 1) {\n            if ($this->CurrCol < $this->NbCol - 1) {\n                // Go to the next column\n                $this->CurrCol++;\n                $this->SetCol($this->CurrCol);\n                $this->y = $this->y0;\n                $this->ChangeColumn = 1; // Number (and direction) of columns changed +1, +2, -2 etc.\n                // DIRECTIONALITY RTL\n                if ($this->directionality == 'rtl') {\n                    $this->ChangeColumn = -($this->ChangeColumn);\n                } // *OTL*\n                // Stay on the page\n                return false;\n            } else {\n                // Go back to the first column - NEW PAGE\n                if (count($this->columnbuffer)) {\n                    $this->printcolumnbuffer();\n                }\n                $this->SetCol(0);\n                $this->y0 = $this->tMargin;\n                $this->ChangeColumn = -($this->NbCol - 1);\n                // DIRECTIONALITY RTL\n                if ($this->directionality == 'rtl') {\n                    $this->ChangeColumn = -($this->ChangeColumn);\n                } // *OTL*\n                // Page break\n                return true;\n            }\n        } /* -- END COLUMNS -- */\n        /* -- TABLES -- */ elseif ($this->table_rotate) {\n            if ($this->tablebuffer) {\n                $this->printtablebuffer();\n            }\n            return true;\n        } /* -- END TABLES -- */ else { // *COLUMNS*\n            $this->ChangeColumn = 0;\n            return $this->autoPageBreak;\n        } // *COLUMNS*\n        return $this->autoPageBreak;\n    }\n\n    // ----------- COLUMNS ---------------------\n    /* -- COLUMNS -- */\n\n    function SetColumns($NbCol, $vAlign = '', $gap = 5)\n    {\n        // NbCol = number of columns\n        // Anything less than 2 turns columns off\n        if ($NbCol < 2) { // SET COLUMNS OFF\n            if ($this->ColActive) {\n                $this->ColActive = 0;\n                if (count($this->columnbuffer)) {\n                    $this->printcolumnbuffer();\n                }\n                $this->NbCol = 1;\n                $this->ResetMargins();\n                $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;\n                $this->divwidth = 0;\n                $this->Ln();\n            }\n            $this->ColActive = 0;\n            $this->columnbuffer = [];\n            $this->ColDetails = [];\n            $this->columnLinks = [];\n            $this->columnAnnots = [];\n            $this->columnForms = [];\n            $this->col_BMoutlines = [];\n            $this->col_toc = [];\n            $this->breakpoints = [];\n        } else { // SET COLUMNS ON\n            if ($this->ColActive) {\n                $this->ColActive = 0;\n                if (count($this->columnbuffer)) {\n                    $this->printcolumnbuffer();\n                }\n                $this->ResetMargins();\n            }\n            if (isset($this->y) && $this->y > $this->tMargin) {\n                $this->Ln();\n            }\n            $this->NbCol = $NbCol;\n            $this->ColGap = $gap;\n            $this->divwidth = 0;\n            $this->ColActive = 1;\n            $this->ColumnAdjust = true; // enables column height adjustment for the page\n            $this->columnbuffer = [];\n            $this->ColDetails = [];\n            $this->columnLinks = [];\n            $this->columnAnnots = [];\n            $this->columnForms = [];\n            $this->col_BMoutlines = [];\n            $this->col_toc = [];\n            $this->breakpoints = [];\n            if ((strtoupper($vAlign) == 'J') || (strtoupper($vAlign) == 'JUSTIFY')) {\n                $vAlign = 'J';\n            } else {\n                $vAlign = '';\n            }\n            $this->colvAlign = $vAlign;\n            // Save the ordinate\n            $absL = $this->DeflMargin - ($gap / 2);\n            $absR = $this->DefrMargin - ($gap / 2);\n            $PageWidth = $this->w - $absL - $absR; // virtual pagewidth for calculation only\n            $ColWidth = (($PageWidth - ($gap * ($NbCol))) / $NbCol);\n            $this->ColWidth = $ColWidth;\n            /* -- OTL -- */\n\n            if ($this->directionality == 'rtl') {\n                for ($i = 0; $i < $this->NbCol; $i++) {\n                    $this->ColL[$i] = $absL + ($gap / 2) + (($NbCol - ($i + 1)) * ($PageWidth / $NbCol));\n                    $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos\n                }\n            } else {\n                /* -- END OTL -- */\n                for ($i = 0; $i < $this->NbCol; $i++) {\n                    $this->ColL[$i] = $absL + ($gap / 2) + ($i * ($PageWidth / $NbCol) );\n                    $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos\n                }\n            } // *OTL*\n            $this->pgwidth = $ColWidth;\n            $this->SetCol(0);\n            $this->y0 = $this->y;\n        }\n        $this->x = $this->lMargin;\n    }\n\n    function SetCol($CurrCol)\n    {\n        // Used internally to set column by number: 0 is 1st column\n        // Set position on a column\n        $this->CurrCol = $CurrCol;\n        $x = $this->ColL[$CurrCol];\n        $xR = $this->ColR[$CurrCol]; // NB This is not R margin -> R pos\n        if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN\n            $x += $this->MarginCorrection;\n            $xR += $this->MarginCorrection;\n        }\n        $this->SetMargins($x, ($this->w - $xR), $this->tMargin);\n    }\n\n    function AddColumn()\n    {\n        $this->NewColumn();\n        $this->ColumnAdjust = false; // disables all column height adjustment for the page.\n    }\n\n    function NewColumn()\n    {\n        if ($this->ColActive == 1) {\n            if ($this->CurrCol < $this->NbCol - 1) {\n                // Go to the next column\n                $this->CurrCol++;\n                $this->SetCol($this->CurrCol);\n                $this->y = $this->y0;\n                $this->ChangeColumn = 1;\n                // DIRECTIONALITY RTL\n                if ($this->directionality == 'rtl') {\n                    $this->ChangeColumn = -($this->ChangeColumn);\n                } // *OTL*\n                // Stay on the page\n            } else {\n                // Go back to the first column\n                // Page break\n                if (count($this->columnbuffer)) {\n                    $this->printcolumnbuffer();\n                }\n                $this->AddPage($this->CurOrientation);\n                $this->SetCol(0);\n                $this->y0 = $this->tMargin;\n                $this->ChangeColumn = -($this->NbCol - 1);\n                // DIRECTIONALITY RTL\n                if ($this->directionality == 'rtl') {\n                    $this->ChangeColumn = -($this->ChangeColumn);\n                } // *OTL*\n            }\n            $this->x = $this->lMargin;\n        } else {\n            $this->AddPage($this->CurOrientation);\n        }\n    }\n\n    function printcolumnbuffer()\n    {\n        // Columns ended (but page not ended) -> try to match all columns - unless disabled by using a custom column-break\n        if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) {\n            // Calculate adjustment to add to each column to calculate rel_y value\n            $this->ColDetails[0]['add_y'] = 0;\n            $last_col = 0;\n            // Recursively add previous column's height\n            for ($i = 1; $i < $this->NbCol; $i++) {\n                if (isset($this->ColDetails[$i]['bottom_margin']) && $this->ColDetails[$i]['bottom_margin']) { // If any entries in the column\n                    $this->ColDetails[$i]['add_y'] = ($this->ColDetails[$i - 1]['bottom_margin'] - $this->y0) + $this->ColDetails[$i - 1]['add_y'];\n                    $last_col = $i;  // Last column actually printed\n                }\n            }\n\n            // Calculate value for each position sensitive entry as though for one column\n            foreach ($this->columnbuffer as $key => $s) {\n                $t = $s['s'];\n                if ($t == 'ACROFORM') {\n                    $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;\n                    $this->columnbuffer[$key]['s'] = '';\n                } elseif (preg_match('/BT \\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) Td/', $t)) {\n                    $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;\n                } elseif (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) \\d+\\.\\d\\d+ [\\-]{0,1}\\d+\\.\\d\\d+ re/', $t)) {\n                    $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;\n                } elseif (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) m/', $t)) {\n                    $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;\n                } elseif (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) l/', $t)) {\n                    $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;\n                } elseif (preg_match('/q \\d+\\.\\d\\d+ 0 0 \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) cm \\/(I|FO)\\d+ Do Q/', $t)) {\n                    $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;\n                } elseif (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ c/', $t)) {\n                    $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;\n                }\n            }\n            foreach ($this->internallink as $key => $f) {\n                if (is_array($f) && isset($f['col'])) {\n                    $this->internallink[$key]['rel_y'] = $f['Y'] + $this->ColDetails[$f['col']]['add_y'] - $this->y0;\n                }\n            }\n\n            $breaks = [];\n            foreach ($this->breakpoints as $c => $bpa) {\n                foreach ($bpa as $rely) {\n                    $breaks[] = $rely + $this->ColDetails[$c]['add_y'] - $this->y0;\n                }\n            }\n\n\n            if (isset($this->ColDetails[$last_col]['bottom_margin'])) {\n                $lcbm = $this->ColDetails[$last_col]['bottom_margin'];\n            } else {\n                $lcbm = 0;\n            }\n            $sum_h = $this->ColDetails[$last_col]['add_y'] + $lcbm - $this->y0;\n            // $sum_h = max($this->ColDetails[$last_col]['add_y'] + $this->ColDetails[$last_col]['bottom_margin'] - $this->y0, end($breaks));\n            $target_h = ($sum_h / $this->NbCol);\n\n            $cbr = [];\n            for ($i = 1; $i < $this->NbCol; $i++) {\n                $th = ($sum_h * $i / $this->NbCol);\n                foreach ($breaks as $bk => $val) {\n                    if ($val > $th) {\n                        if (($val - $th) < ($th - $breaks[$bk - 1])) {\n                            $cbr[$i - 1] = $val;\n                        } else {\n                            $cbr[$i - 1] = $breaks[$bk - 1];\n                        }\n                        break;\n                    }\n                }\n            }\n            $cbr[($this->NbCol - 1)] = $sum_h;\n\n            // mPDF 6\n            // Avoid outputing with 1st column empty\n            if (isset($cbr[0]) && $cbr[0] == 0) {\n                for ($i = 0; $i < $this->NbCol - 1; $i++) {\n                    $cbr[$i] = $cbr[$i + 1];\n                }\n            }\n\n            // Now update the columns - divide into columns of approximately equal value\n            $last_new_col = 0;\n            $yadj = 0; // mm\n            $xadj = 0;\n            $last_col_bottom = 0;\n            $lowest_bottom_y = 0;\n            $block_bottom = 0;\n            $newcolumn = 0;\n            foreach ($this->columnbuffer as $key => $s) {\n                if (isset($s['rel_y'])) { // only process position sensitive data\n                    if ($s['rel_y'] >= $cbr[$newcolumn]) {\n                        $newcolumn++;\n                    } else {\n                        $newcolumn = $last_new_col;\n                    }\n\n\n                    $block_bottom = max($block_bottom, ($s['rel_y'] + $s['h']));\n\n                    if ($this->directionality == 'rtl') { // *OTL*\n                        $xadj = -(($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap)); // *OTL*\n                    } // *OTL*\n                    else { // *OTL*\n                        $xadj = ($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap);\n                    } // *OTL*\n\n                    if ($last_new_col != $newcolumn) { // Added new column\n                        $last_col_bottom = $this->columnbuffer[$key]['rel_y'];\n                        $block_bottom = 0;\n                    }\n                    $yadj = ($s['rel_y'] - $s['y']) - ($last_col_bottom) + $this->y0;\n                    // callback function\n                    $t = $s['s'];\n\n                    // mPDF 5.7+\n                    $t = $this->columnAdjustPregReplace('Td', $xadj, $yadj, '/BT (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) Td/', $t);\n                    $t = $this->columnAdjustPregReplace('re', $xadj, $yadj, '/(\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) ([\\-]{0,1}\\d+\\.\\d\\d+) re/', $t);\n                    $t = $this->columnAdjustPregReplace('l', $xadj, $yadj, '/(\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) l/', $t);\n                    $t = $this->columnAdjustPregReplace('img', $xadj, $yadj, '/q (\\d+\\.\\d\\d+) 0 0 (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) cm \\/(I|FO)/', $t);\n                    $t = $this->columnAdjustPregReplace('draw', $xadj, $yadj, '/(\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) m/', $t);\n                    $t = $this->columnAdjustPregReplace('bezier', $xadj, $yadj, '/(\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) (\\d+\\.\\d\\d+) c/', $t);\n\n                    $this->columnbuffer[$key]['s'] = $t;\n                    $this->columnbuffer[$key]['newcol'] = $newcolumn;\n                    $this->columnbuffer[$key]['newy'] = $s['y'] + $yadj;\n                    $last_new_col = $newcolumn;\n                    $clb = $s['y'] + $yadj + $s['h']; // bottom_margin of current\n                    if ((isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb > $this->ColDetails[$newcolumn]['max_bottom']) || (!isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb)) {\n                        $this->ColDetails[$newcolumn]['max_bottom'] = $clb;\n                    }\n                    if ($clb > $lowest_bottom_y) {\n                        $lowest_bottom_y = $clb;\n                    }\n                    // Adjust LINKS\n                    if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                        $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];\n                        $this->PageLinks[$this->page][$ref][0] += ($xadj * Mpdf::SCALE);\n                        $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE);\n                        unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);\n                    }\n                    // Adjust FORM FIELDS\n                    if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                        $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];\n                        $this->form->forms[$ref]['x'] += ($xadj);\n                        $this->form->forms[$ref]['y'] += ($yadj);\n                        unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);\n                    }\n                    /* -- ANNOTATIONS -- */\n                    if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                        $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];\n                        if ($this->PageAnnots[$this->page][$ref]['x'] < 0) {\n                            $this->PageAnnots[$this->page][$ref]['x'] -= ($xadj);\n                        } else {\n                            $this->PageAnnots[$this->page][$ref]['x'] += ($xadj);\n                        }\n                        $this->PageAnnots[$this->page][$ref]['y'] += ($yadj); // unlike PageLinks, Page annots has y values from top in mm\n                        unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);\n                    }\n                    /* -- END ANNOTATIONS -- */\n                }\n            }\n\n            /* -- BOOKMARKS -- */\n            // Adjust Bookmarks\n            foreach ($this->col_BMoutlines as $v) {\n                $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];\n            }\n            /* -- END BOOKMARKS -- */\n\n            /* -- TOC -- */\n\n            // Adjust ToC\n            foreach ($this->col_toc as $v) {\n                $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];\n                $this->links[$v['link']][1] = $this->y0;\n            }\n            /* -- END TOC -- */\n\n            // Adjust column length to be equal\n            if ($this->colvAlign == 'J') {\n                foreach ($this->columnbuffer as $key => $s) {\n                    if (isset($s['rel_y'])) { // only process position sensitive data\n                        // Set ratio to expand y values or heights\n                        if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {\n                            $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));\n                        } else {\n                            $ratio = 1;\n                        }\n                        if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {\n                            $yadj = ($s['newy'] - $this->y0) * ($ratio - 1);\n\n                            // Adjust LINKS\n                            if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                                $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];\n                                $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value\n                                $this->PageLinks[$this->page][$ref][3] *= $ratio; // height\n                                unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);\n                            }\n                            // Adjust FORM FIELDS\n                            if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                                $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];\n                                $this->form->forms[$ref]['x'] += ($xadj);\n                                $this->form->forms[$ref]['y'] += ($yadj);\n                                unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);\n                            }\n                            /* -- ANNOTATIONS -- */\n                            if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                                $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];\n                                $this->PageAnnots[$this->page][$ref]['y'] += ($yadj);\n                                unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);\n                            }\n                            /* -- END ANNOTATIONS -- */\n                        }\n                    }\n                }\n                foreach ($this->internallink as $key => $f) {\n                    if (is_array($f) && isset($f['col'])) {\n                        $last_col_bottom = 0;\n                        for ($nbc = 0; $nbc < $this->NbCol; $nbc++) {\n                            if ($f['rel_y'] >= $cbr[$nbc]) {\n                                $last_col_bottom = $cbr[$nbc];\n                            }\n                        }\n                        $yadj = ($f['rel_y'] - $f['Y']) - $last_col_bottom + $this->y0;\n                        $f['Y'] += $yadj;\n                        unset($f['col']);\n                        unset($f['rel_y']);\n                        $this->internallink[$key] = $f;\n                    }\n                }\n\n                $last_col = -1;\n                $trans_on = false;\n                foreach ($this->columnbuffer as $key => $s) {\n                    if (isset($s['rel_y'])) { // only process position sensitive data\n                        // Set ratio to expand y values or heights\n                        if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {\n                            $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));\n                        } else {\n                            $ratio = 1;\n                        }\n                        if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {\n                            // Start Transformation\n                            $this->pages[$this->page] .= $this->StartTransform(true) . \"\\n\";\n                            $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . \"\\n\";\n                            $trans_on = true;\n                        }\n                    }\n                    // Now output the adjusted values\n                    $this->pages[$this->page] .= $s['s'] . \"\\n\";\n                    if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) { // only process position sensitive data\n                        // Stop Transformation\n                        $this->pages[$this->page] .= $this->StopTransform(true) . \"\\n\";\n                        $trans_on = false;\n                    }\n                }\n                if ($trans_on) {\n                    $this->pages[$this->page] .= $this->StopTransform(true) . \"\\n\";\n                }\n            } else { // if NOT $this->colvAlign == 'J'\n                // Now output the adjusted values\n                foreach ($this->columnbuffer as $s) {\n                    $this->pages[$this->page] .= $s['s'] . \"\\n\";\n                }\n            }\n            if ($lowest_bottom_y > 0) {\n                $this->y = $lowest_bottom_y;\n            }\n        } // Columns not ended but new page -> align columns (can leave the columns alone - just tidy up the height)\n        elseif ($this->colvAlign == 'J' && $this->ColumnAdjust && !$this->keepColumns) {\n            // calculate the lowest bottom margin\n            $lowest_bottom_y = 0;\n            foreach ($this->columnbuffer as $key => $s) {\n                // Only process output data\n                $t = $s['s'];\n                if ($t == 'ACROFORM' || (preg_match('/BT \\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) Td/', $t)) || (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) \\d+\\.\\d\\d+ [\\-]{0,1}\\d+\\.\\d\\d+ re/', $t)) ||\n                    (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) l/', $t)) ||\n                    (preg_match('/q \\d+\\.\\d\\d+ 0 0 \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) cm \\/(I|FO)\\d+ Do Q/', $t)) ||\n                    (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) m/', $t)) ||\n                    (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ c/', $t))) {\n                    $clb = $s['y'] + $s['h'];\n                    if ((isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom']) || !isset($this->ColDetails[$s['col']]['max_bottom'])) {\n                        $this->ColDetails[$s['col']]['max_bottom'] = $clb;\n                    }\n                    if ($clb > $lowest_bottom_y) {\n                        $lowest_bottom_y = $clb;\n                    }\n                    $this->columnbuffer[$key]['rel_y'] = $s['y']; // Marks position sensitive data to process later\n                    if ($t == 'ACROFORM') {\n                        $this->columnbuffer[$key]['s'] = '';\n                    }\n                }\n            }\n            // Adjust column length equal\n            foreach ($this->columnbuffer as $key => $s) {\n                // Set ratio to expand y values or heights\n                if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {\n                    $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));\n                } else {\n                    $ratio = 1;\n                }\n                if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {\n                    $yadj = ($s['y'] - $this->y0) * ($ratio - 1);\n\n                    // Adjust LINKS\n                    if (isset($s['rel_y'])) { // only process position sensitive data\n                        // otherwise triggers for all entries in column buffer (.e.g. formatting) and makes below adjustments more than once\n                        if (isset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                            $ref = $this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])];\n                            $this->PageLinks[$this->page][$ref][1] -= ($yadj * Mpdf::SCALE); // y value\n                            $this->PageLinks[$this->page][$ref][3] *= $ratio; // height\n                            unset($this->columnLinks[$s['col']][intval($s['x'])][intval($s['y'])]);\n                        }\n                        // Adjust FORM FIELDS\n                        if (isset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                            $ref = $this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])];\n                            $this->form->forms[$ref]['x'] += ($xadj);\n                            $this->form->forms[$ref]['y'] += ($yadj);\n                            unset($this->columnForms[$s['col']][intval($s['x'])][intval($s['y'])]);\n                        }\n                        /* -- ANNOTATIONS -- */\n                        if (isset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])])) {\n                            $ref = $this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])];\n                            $this->PageAnnots[$this->page][$ref]['y'] += ($yadj);\n                            unset($this->columnAnnots[$s['col']][intval($s['x'])][intval($s['y'])]);\n                        }\n                        /* -- END ANNOTATIONS -- */\n                    }\n                }\n            }\n\n            /* -- BOOKMARKS -- */\n\n            // Adjust Bookmarks\n            foreach ($this->col_BMoutlines as $v) {\n                $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']];\n            }\n            /* -- END BOOKMARKS -- */\n\n            /* -- TOC -- */\n\n            // Adjust ToC\n            foreach ($this->col_toc as $v) {\n                $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];\n                $this->links[$v['link']][1] = $this->y0;\n            }\n            /* -- END TOC -- */\n\n            $trans_on = false;\n            foreach ($this->columnbuffer as $key => $s) {\n\n                if (isset($s['rel_y'])) { // only process position sensitive data\n\n                    // Set ratio to expand y values or heights\n                    if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {\n                        $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));\n                    } else {\n                        $ratio = 1;\n                    }\n\n                    if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {\n                        // Start Transformation\n                        $this->pages[$this->page] .= $this->StartTransform(true) . \"\\n\";\n                        $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . \"\\n\";\n                        $trans_on = true;\n                    }\n                }\n\n                // Now output the adjusted values\n                $this->pages[$this->page] .= $s['s'] . \"\\n\";\n                if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) {\n                    // Stop Transformation\n                    $this->pages[$this->page] .= $this->StopTransform(true) . \"\\n\";\n                    $trans_on = false;\n                }\n            }\n\n            if ($trans_on) {\n                $this->pages[$this->page] .= $this->StopTransform(true) . \"\\n\";\n            }\n\n            if ($lowest_bottom_y > 0) {\n                $this->y = $lowest_bottom_y;\n            }\n\n        } else { // Just reproduce the page as it was\n\n            // If page has not ended but height adjustment was disabled by custom column-break - adjust y\n            $lowest_bottom_y = 0;\n\n            if (!$this->ColActive && (!$this->ColumnAdjust || $this->keepColumns)) {\n\n                // calculate the lowest bottom margin\n                foreach ($this->columnbuffer as $key => $s) {\n\n                    // Only process output data\n                    $t = $s['s'];\n                    if ($t === 'ACROFORM'\n                        || (preg_match('/BT \\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) Td/', $t))\n                        || (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) \\d+\\.\\d\\d+ [\\-]{0,1}\\d+\\.\\d\\d+ re/', $t))\n                        || (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) l/', $t))\n                        || (preg_match('/q \\d+\\.\\d\\d+ 0 0 \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) cm \\/(I|FO)\\d+ Do Q/', $t))\n                        || (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) m/', $t))\n                        || (preg_match('/\\d+\\.\\d\\d+ (\\d+\\.\\d\\d+) \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ \\d+\\.\\d\\d+ c/', $t))) {\n\n                        $clb = $s['y'] + $s['h'];\n\n                        if (isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom'] || (!isset($this->ColDetails[$s['col']]['max_bottom']) && $clb)) {\n                            $this->ColDetails[$s['col']]['max_bottom'] = $clb;\n                        }\n\n                        if ($clb > $lowest_bottom_y) {\n                            $lowest_bottom_y = $clb;\n                        }\n                    }\n                }\n            }\n\n            foreach ($this->columnbuffer as $key => $s) {\n                if ($s['s'] != 'ACROFORM') {\n                    $this->pages[$this->page] .= $s['s'] . \"\\n\";\n                }\n            }\n\n            if ($lowest_bottom_y > 0) {\n                $this->y = $lowest_bottom_y;\n            }\n\n            /* -- BOOKMARKS -- */\n            // Output Bookmarks\n            foreach ($this->col_BMoutlines as $v) {\n                $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];\n            }\n            /* -- END BOOKMARKS -- */\n\n            /* -- TOC -- */\n            // Output ToC\n            foreach ($this->col_toc as $v) {\n                $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];\n            }\n            /* -- END TOC -- */\n        }\n\n        foreach ($this->internallink as $key => $f) {\n\n            if (isset($this->internallink[$key]['col'])) {\n                unset($this->internallink[$key]['col']);\n            }\n\n            if (isset($this->internallink[$key]['rel_y'])) {\n                unset($this->internallink[$key]['rel_y']);\n            }\n        }\n\n        $this->columnbuffer = [];\n        $this->ColDetails = [];\n        $this->columnLinks = [];\n        $this->columnAnnots = [];\n        $this->columnForms = [];\n\n        $this->col_BMoutlines = [];\n        $this->col_toc = [];\n        $this->breakpoints = [];\n    }\n\n    // mPDF 5.7+\n    function columnAdjustPregReplace($type, $xadj, $yadj, $pattern, $subject)\n    {\n        preg_match($pattern, $subject, $matches);\n\n        if (!count($matches)) {\n            return $subject;\n        }\n\n        if (!isset($matches[3])) {\n            $matches[3] = 0;\n        }\n\n        if (!isset($matches[4])) {\n            $matches[4] = 0;\n        }\n\n        if (!isset($matches[5])) {\n            $matches[5] = 0;\n        }\n\n        if (!isset($matches[6])) {\n            $matches[6] = 0;\n        }\n\n        return str_replace($matches[0], $this->columnAdjustAdd($type, Mpdf::SCALE, $xadj, $yadj, $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]), $subject);\n    }\n    /* -- END COLUMNS -- */\n\n    // ==================================================================\n    /* -- TABLES -- */\n    function printcellbuffer()\n    {\n        if (count($this->cellBorderBuffer)) {\n\n            sort($this->cellBorderBuffer);\n\n            foreach ($this->cellBorderBuffer as $cbb) {\n\n                $cba = unpack(\"A16dom/nbord/A1side/ns/dbw/a6ca/A10style/dx/dy/dw/dh/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd/dover/\", $cbb);\n                $side = $cba['side'];\n                $color = str_pad($cba['ca'], 6, \"\\x00\");\n\n                $details = [];\n\n                $details[$side]['dom'] = (float) $cba['dom'];\n                $details[$side]['s'] = $cba['s'];\n                $details[$side]['w'] = $cba['bw'];\n                $details[$side]['c'] = $color;\n                $details[$side]['style'] = trim($cba['style']);\n\n                $details['mbw']['BL'] = $cba['mbl'];\n                $details['mbw']['BR'] = $cba['mbr'];\n                $details['mbw']['RT'] = $cba['mrt'];\n                $details['mbw']['RB'] = $cba['mrb'];\n                $details['mbw']['TL'] = $cba['mtl'];\n                $details['mbw']['TR'] = $cba['mtr'];\n                $details['mbw']['LT'] = $cba['mlt'];\n                $details['mbw']['LB'] = $cba['mlb'];\n\n                $details['cellposdom'] = $cba['cpd'];\n\n                $details['p'] = $side;\n\n                if ($cba['over'] == 1) {\n                    $details[$side]['overlay'] = true;\n                } else {\n                    $details[$side]['overlay'] = false;\n                }\n\n                $this->_tableRect($cba['x'], $cba['y'], $cba['w'], $cba['h'], $cba['bord'], $details, false, false);\n            }\n\n            $this->cellBorderBuffer = [];\n        }\n    }\n\n    // ==================================================================\n    function printtablebuffer()\n    {\n\n        if (!$this->table_rotate) {\n\n            $this->pages[$this->page] .= $this->tablebuffer;\n\n            foreach ($this->tbrot_Links as $p => $l) {\n                foreach ($l as $v) {\n                    $this->PageLinks[$p][] = $v;\n                }\n            }\n            $this->tbrot_Links = [];\n\n            /* -- ANNOTATIONS -- */\n            foreach ($this->tbrot_Annots as $p => $l) {\n                foreach ($l as $v) {\n                    $this->PageAnnots[$p][] = $v;\n                }\n            }\n            $this->tbrot_Annots = [];\n            /* -- END ANNOTATIONS -- */\n\n            /* -- BOOKMARKS -- */\n            // Output Bookmarks\n            foreach ($this->tbrot_BMoutlines as $v) {\n                $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];\n            }\n            $this->tbrot_BMoutlines = [];\n            /* -- END BOOKMARKS -- */\n\n            /* -- TOC -- */\n            // Output ToC\n            foreach ($this->tbrot_toc as $v) {\n                $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];\n            }\n            $this->tbrot_toc = [];\n            /* -- END TOC -- */\n\n            return;\n        }\n\n        // elseif rotated\n        $lm = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left'];\n        $pw = $this->blk[$this->blklvl]['inner_width'];\n\n        // Start Transformation\n        $this->pages[$this->page] .= $this->StartTransform(true) . \"\\n\";\n\n        if ($this->table_rotate > 1) { // clockwise\n\n            if ($this->tbrot_align == 'L') {\n                $xadj = $this->tbrot_h; // align L (as is)\n            } elseif ($this->tbrot_align == 'R') {\n                $xadj = $lm - $this->tbrot_x0 + ($pw); // align R\n            } else {\n                $xadj = $lm - $this->tbrot_x0 + (($pw + $this->tbrot_h) / 2); // align C\n            }\n\n            $yadj = 0;\n\n        } else { // anti-clockwise\n\n            if ($this->tbrot_align == 'L') {\n                $xadj = 0; // align L (as is)\n            } elseif ($this->tbrot_align == 'R') {\n                $xadj = $lm - $this->tbrot_x0 + ($pw - $this->tbrot_h); // align R\n            } else {\n                $xadj = $lm - $this->tbrot_x0 + (($pw - $this->tbrot_h) / 2); // align C\n            }\n\n            $yadj = $this->tbrot_w;\n        }\n\n\n        $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . \"\\n\";\n        $this->pages[$this->page] .= $this->transformRotate($this->table_rotate, $this->tbrot_x0, $this->tbrot_y0, true) . \"\\n\";\n\n        // Now output the adjusted values\n        $this->pages[$this->page] .= $this->tablebuffer;\n\n        foreach ($this->tbrot_Links as $p => $l) {\n\n            foreach ($l as $v) {\n\n                $w = $v[2] / Mpdf::SCALE;\n                $h = $v[3] / Mpdf::SCALE;\n                $ax = ($v[0] / Mpdf::SCALE) - $this->tbrot_x0;\n                $ay = (($this->hPt - $v[1]) / Mpdf::SCALE) - $this->tbrot_y0;\n\n                if ($this->table_rotate > 1) { // clockwise\n                    $bx = $this->tbrot_x0 + $xadj - $ay - $h;\n                    $by = $this->tbrot_y0 + $yadj + $ax;\n                } else {\n                    $bx = $this->tbrot_x0 + $xadj + $ay;\n                    $by = $this->tbrot_y0 + $yadj - $ax - $w;\n                }\n\n                $v[0] = $bx * Mpdf::SCALE;\n                $v[1] = ($this->h - $by) * Mpdf::SCALE;\n                $v[2] = $h * Mpdf::SCALE; // swap width and height\n                $v[3] = $w * Mpdf::SCALE;\n\n                $this->PageLinks[$p][] = $v;\n            }\n        }\n\n        $this->tbrot_Links = [];\n        foreach ($this->internallink as $key => $f) {\n            if (is_array($f) && isset($f['tbrot'])) {\n                $f['Y'] = $this->tbrot_y0;\n                $f['PAGE'] = $this->page;\n                unset($f['tbrot']);\n                $this->internallink[$key] = $f;\n            }\n        }\n\n        /* -- ANNOTATIONS -- */\n        foreach ($this->tbrot_Annots as $p => $l) {\n            foreach ($l as $v) {\n                $ax = abs($v['x']) - $this->tbrot_x0; // abs because -ve values are internally set and held for reference if annotMargin set\n                $ay = $v['y'] - $this->tbrot_y0;\n\n                if ($this->table_rotate > 1) { // clockwise\n                    $bx = $this->tbrot_x0 + $xadj - $ay;\n                    $by = $this->tbrot_y0 + $yadj + $ax;\n                } else {\n                    $bx = $this->tbrot_x0 + $xadj + $ay;\n                    $by = $this->tbrot_y0 + $yadj - $ax;\n                }\n\n                if ($v['x'] < 0) {\n                    $v['x'] = -$bx;\n                } else {\n                    $v['x'] = $bx;\n                }\n\n                $v['y'] = ($by);\n                $this->PageAnnots[$p][] = $v;\n            }\n        }\n\n        $this->tbrot_Annots = [];\n        /* -- END ANNOTATIONS -- */\n\n        /* -- BOOKMARKS -- */\n        // Adjust Bookmarks\n        foreach ($this->tbrot_BMoutlines as $v) {\n            $v['y'] = $this->tbrot_y0;\n            $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];\n        }\n        /* -- END BOOKMARKS -- */\n\n        /* -- TOC -- */\n        // Adjust ToC - uses document page number\n        foreach ($this->tbrot_toc as $v) {\n            $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];\n            $this->links[$v['link']][1] = $this->tbrot_y0;\n        }\n        /* -- END TOC -- */\n\n        $this->tbrot_BMoutlines = [];\n        $this->tbrot_toc = [];\n\n        // Stop Transformation\n        $this->pages[$this->page] .= $this->StopTransform(true) . \"\\n\";\n\n        $this->y = $this->tbrot_y0 + $this->tbrot_w;\n        $this->x = $this->lMargin;\n\n        $this->tablebuffer = '';\n    }\n\n    /**\n     * Keep-with-table This buffers contents of h1-6 to keep on page with table\n     */\n    function printkwtbuffer()\n    {\n        if (!$this->kwt_moved) {\n\n            foreach ($this->kwt_buffer as $s) {\n                $this->pages[$this->page] .= $s['s'] . \"\\n\";\n            }\n\n            foreach ($this->kwt_Links as $p => $l) {\n                foreach ($l as $v) {\n                    $this->PageLinks[$p][] = $v;\n                }\n            }\n\n            $this->kwt_Links = [];\n\n            /* -- ANNOTATIONS -- */\n            foreach ($this->kwt_Annots as $p => $l) {\n                foreach ($l as $v) {\n                    $this->PageAnnots[$p][] = $v;\n                }\n            }\n            $this->kwt_Annots = [];\n            /* -- END ANNOTATIONS -- */\n\n            /* -- INDEX -- */\n            // Output Reference (index)\n            foreach ($this->kwt_Reference as $v) {\n\n                $Present = 0;\n\n                for ($i = 0; $i < count($this->Reference); $i++) {\n                    if ($this->Reference[$i]['t'] == $v['t']) {\n                        $Present = 1;\n                        if (!in_array($v['op'], $this->Reference[$i]['p'])) {\n                            $this->Reference[$i]['p'][] = $v['op'];\n                        }\n                    }\n                }\n\n                if ($Present == 0) {\n                    $this->Reference[] = ['t' => $v['t'], 'p' => [$v['op']]];\n                }\n            }\n            $this->kwt_Reference = [];\n            /* -- END INDEX -- */\n\n            /* -- BOOKMARKS -- */\n            // Output Bookmarks\n            foreach ($this->kwt_BMoutlines as $v) {\n                $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']];\n            }\n            $this->kwt_BMoutlines = [];\n            /* -- END BOOKMARKS -- */\n\n            /* -- TOC -- */\n            // Output ToC\n            foreach ($this->kwt_toc as $v) {\n                $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']];\n            }\n            $this->kwt_toc = [];\n            /* -- END TOC -- */\n\n            $this->pageoutput[$this->page] = []; // mPDF 6\n\n            return;\n        }\n\n        // Start Transformation\n        $this->pages[$this->page] .= $this->StartTransform(true) . \"\\n\";\n        $xadj = $this->lMargin - $this->kwt_x0;\n        // $yadj = $this->y - $this->kwt_y0 ;\n        $yadj = $this->tMargin - $this->kwt_y0;\n\n        $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . \"\\n\";\n\n        // Now output the adjusted values\n        foreach ($this->kwt_buffer as $s) {\n            $this->pages[$this->page] .= $s['s'] . \"\\n\";\n        }\n\n        // Adjust hyperLinks\n        foreach ($this->kwt_Links as $p => $l) {\n            foreach ($l as $v) {\n                $bx = $this->kwt_x0 + $xadj;\n                $by = $this->kwt_y0 + $yadj;\n                $v[0] = $bx * Mpdf::SCALE;\n                $v[1] = ($this->h - $by) * Mpdf::SCALE;\n                $this->PageLinks[$p][] = $v;\n            }\n        }\n\n        foreach ($this->internallink as $key => $f) {\n            if (is_array($f) && isset($f['kwt'])) {\n                $f['Y'] += $yadj;\n                $f['PAGE'] = $this->page;\n                unset($f['kwt']);\n                $this->internallink[$key] = $f;\n            }\n        }\n\n        /* -- ANNOTATIONS -- */\n        foreach ($this->kwt_Annots as $p => $l) {\n            foreach ($l as $v) {\n                $bx = $this->kwt_x0 + $xadj;\n                $by = $this->kwt_y0 + $yadj;\n                if ($v['x'] < 0) {\n                    $v['x'] = -$bx;\n                } else {\n                    $v['x'] = $bx;\n                }\n                $v['y'] = $by;\n                $this->PageAnnots[$p][] = $v;\n            }\n        }\n        /* -- END ANNOTATIONS -- */\n\n        /* -- BOOKMARKS -- */\n\n        // Adjust Bookmarks\n        foreach ($this->kwt_BMoutlines as $v) {\n            if ($v['y'] != 0) {\n                $v['y'] += $yadj;\n            }\n            $this->BMoutlines[] = ['t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page];\n        }\n        /* -- END BOOKMARKS -- */\n\n        /* -- INDEX -- */\n        // Adjust Reference (index)\n        foreach ($this->kwt_Reference as $v) {\n\n            $Present = 0;\n\n            // Search the reference (AND Ref/PageNo) in the array\n            for ($i = 0; $i < count($this->Reference); $i++) {\n                if ($this->Reference[$i]['t'] == $v['t']) {\n                    $Present = 1;\n                    if (!in_array($this->page, $this->Reference[$i]['p'])) {\n                        $this->Reference[$i]['p'][] = $this->page;\n                    }\n                }\n            }\n\n            if ($Present == 0) {\n                $this->Reference[] = ['t' => $v['t'], 'p' => [$this->page]];\n            }\n        }\n        /* -- END INDEX -- */\n\n        /* -- TOC -- */\n\n        // Adjust ToC\n        foreach ($this->kwt_toc as $v) {\n            $this->tableOfContents->_toc[] = ['t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']];\n            $this->links[$v['link']][0] = $this->page;\n            $this->links[$v['link']][1] += $yadj;\n        }\n        /* -- END TOC -- */\n\n\n        $this->kwt_Links = [];\n        $this->kwt_Annots = [];\n\n        $this->kwt_Reference = [];\n        $this->kwt_BMoutlines = [];\n        $this->kwt_toc = [];\n\n        // Stop Transformation\n        $this->pages[$this->page] .= $this->StopTransform(true) . \"\\n\";\n\n        $this->kwt_buffer = [];\n\n        $this->y += $this->kwt_height;\n        $this->pageoutput[$this->page] = []; // mPDF 6\n    }\n    /* -- END TABLES -- */\n\n    function printfloatbuffer()\n    {\n        if (count($this->floatbuffer)) {\n            $this->objectbuffer = $this->floatbuffer;\n            $this->printobjectbuffer(false);\n            $this->objectbuffer = [];\n            $this->floatbuffer = [];\n            $this->floatmargins = [];\n        }\n    }\n\n    function Circle($x, $y, $r, $style = 'S')\n    {\n        $this->Ellipse($x, $y, $r, $r, $style);\n    }\n\n    function Ellipse($x, $y, $rx, $ry, $style = 'S')\n    {\n        if ($style === 'F') {\n            $op = 'f';\n        } elseif ($style === 'FD' or $style === 'DF') {\n            $op = 'B';\n        } else {\n            $op = 'S';\n        }\n\n        $lx = 4 / 3 * (M_SQRT2 - 1) * $rx;\n        $ly = 4 / 3 * (M_SQRT2 - 1) * $ry;\n\n        $h = $this->h;\n\n        $this->writer->write(sprintf('%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c', ($x + $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - ($y - $ly)) * Mpdf::SCALE, ($x + $lx) * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE, $x * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE));\n        $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $lx) * Mpdf::SCALE, ($h - ($y - $ry)) * Mpdf::SCALE, ($x - $rx) * Mpdf::SCALE, ($h - ($y - $ly)) * Mpdf::SCALE, ($x - $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE));\n        $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $rx) * Mpdf::SCALE, ($h - ($y + $ly)) * Mpdf::SCALE, ($x - $lx) * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE, $x * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE));\n        $this->writer->write(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c %s', ($x + $lx) * Mpdf::SCALE, ($h - ($y + $ry)) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - ($y + $ly)) * Mpdf::SCALE, ($x + $rx) * Mpdf::SCALE, ($h - $y) * Mpdf::SCALE, $op));\n    }\n\n    /* -- DIRECTW -- */\n    function AutosizeText($text, $w, $font, $style, $szfont = 72)\n    {\n\n        $text = ' ' . $text . ' ';\n\n        $this->SetFont($font, $style, $szfont, false);\n\n        $text = $this->purify_utf8_text($text);\n        if ($this->text_input_as_HTML) {\n            $text = $this->all_entities_to_utf8($text);\n        }\n        if ($this->usingCoreFont) {\n            $text = mb_convert_encoding($text, $this->mb_enc, 'UTF-8');\n        }\n\n        // DIRECTIONALITY\n        if (preg_match(\"/([\" . $this->pregRTLchars . \"])/u\", $text)) {\n            $this->biDirectional = true;\n        }\n\n        $textvar = 0;\n        $save_OTLtags = $this->OTLtags;\n        $this->OTLtags = [];\n\n        if ($this->useKerning) {\n            if ($this->CurrentFont['haskernGPOS']) {\n                $this->OTLtags['Plus'] .= ' kern';\n            } else {\n                $textvar = ($textvar | TextVars::FC_KERNING);\n            }\n        }\n\n        /* -- OTL -- */\n        // Use OTL OpenType Table Layout - GSUB & GPOS\n        if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {\n            $text = $this->otl->applyOTL($text, $this->CurrentFont['useOTL']);\n            $OTLdata = $this->otl->OTLdata;\n        }\n        /* -- END OTL -- */\n\n        $this->OTLtags = $save_OTLtags;\n\n        $this->magic_reverse_dir($text, $this->directionality, $OTLdata);\n\n        $width = $this->sizeConverter->convert($w);\n        $loop = 0;\n\n        while ($loop == 0) {\n\n            $this->SetFont($font, $style, $szfont, false);\n            $sz = $this->GetStringWidth($text, true, $OTLdata, $textvar);\n\n            if ($sz > $w) {\n                $szfont --;\n            } else {\n                $loop ++;\n            }\n        }\n\n        $this->SetFont($font, $style, $szfont, true, true);\n        $this->Cell($w, 0, $text, 0, 0, \"C\", 0, '', 0, 0, 0, 'M', 0, false, $OTLdata, $textvar);\n    }\n    /* -- END DIRECTW -- */\n\n    // ====================================================\n    // ====================================================\n\n    function magic_reverse_dir(&$chunk, $dir, &$chunkOTLdata)\n    {\n        /* -- OTL -- */\n        if ($this->usingCoreFont) {\n            return 0;\n        }\n\n        if ($chunk == '') {\n            return 0;\n        }\n\n        if ($this->biDirectional || $dir == 'rtl') {\n\n            // check if string contains RTL text\n            // including any added from OTL tables (in PUA)\n            $pregRTLchars = $this->pregRTLchars;\n\n            if (isset($this->CurrentFont['rtlPUAstr']) && $this->CurrentFont['rtlPUAstr']) {\n                $pregRTLchars .= $this->CurrentFont['rtlPUAstr'];\n            }\n\n            if (!preg_match(\"/[\" . $pregRTLchars . \"]/u\", $chunk) && $dir != 'rtl') {\n                return 0;\n            }   // Chunk doesn't contain RTL characters\n\n            $unicode = $this->UTF8StringToArray($chunk, false);\n\n            $isStrong = false;\n            if (empty($chunkOTLdata)) {\n                $this->getBasicOTLdata($chunkOTLdata, $unicode, $isStrong);\n            }\n\n            $useGPOS = isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0x80);\n\n            // NB Returned $chunk may be a shorter string (with adjusted $cOTLdata) by removal of LRE, RLE etc embedding codes.\n            [$chunk, $rtl_content] = $this->otl->bidiSort($unicode, $chunk, $dir, $chunkOTLdata, $useGPOS);\n\n            return $rtl_content;\n        }\n\n        /* -- END OTL -- */\n        return 0;\n    }\n\n    /* -- OTL -- */\n\n    function getBasicOTLdata(&$chunkOTLdata, $unicode, &$is_strong)\n    {\n        if (empty($this->otl)) {\n            $this->otl = new Otl($this, $this->fontCache);\n        }\n\n        $chunkOTLdata['group'] = '';\n        $chunkOTLdata['GPOSinfo'] = [];\n        $chunkOTLdata['char_data'] = [];\n\n        foreach ($unicode as $char) {\n\n            $ucd_record = Ucdn::get_ucd_record($char);\n            $chunkOTLdata['char_data'][] = ['bidi_class' => $ucd_record[2], 'uni' => $char];\n\n            if ($ucd_record[2] == 0 || $ucd_record[2] == 3 || $ucd_record[2] == 4) {\n                $is_strong = true;\n            } // contains strong character\n\n            if ($ucd_record[0] == Ucdn::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {\n                $chunkOTLdata['group'] .= 'M';\n            } elseif ($char == 32 || $char == 12288) {\n                $chunkOTLdata['group'] .= 'S';\n            } else {\n                $chunkOTLdata['group'] .= 'C';\n            }\n        }\n    }\n\n    function _setBidiCodes($mode = 'start', $bdf = '')\n    {\n        $s = '';\n\n        if ($mode == 'end') {\n\n            // PDF comes before PDI to close isolate-override (e.g. \"LRILROPDFPDI\")\n            if (strpos($bdf, 'PDF') !== false) {\n                $s .= UtfString::code2utf(0x202C);\n            } // POP DIRECTIONAL FORMATTING\n\n            if (strpos($bdf, 'PDI') !== false) {\n                $s .= UtfString::code2utf(0x2069);\n            } // POP DIRECTIONAL ISOLATE\n\n        } elseif ($mode == 'start') {\n\n            // LRI comes before LRO to open isolate-override (e.g. \"LRILROPDFPDI\")\n            if (strpos($bdf, 'LRI') !== false) {  // U+2066 LRI\n                $s .= UtfString::code2utf(0x2066);\n            } elseif (strpos($bdf, 'RLI') !== false) { // U+2067 RLI\n                $s .= UtfString::code2utf(0x2067);\n            } elseif (strpos($bdf, 'FSI') !== false) { // U+2068 FSI\n                $s .= UtfString::code2utf(0x2068);\n            }\n\n            if (strpos($bdf, 'LRO') !== false) { // U+202D LRO\n                $s .= UtfString::code2utf(0x202D);\n            } elseif (strpos($bdf, 'RLO') !== false) { // U+202E RLO\n                $s .= UtfString::code2utf(0x202E);\n            } elseif (strpos($bdf, 'LRE') !== false) { // U+202A LRE\n                $s .= UtfString::code2utf(0x202A);\n            } elseif (strpos($bdf, 'RLE') !== false) { // U+202B RLE\n                $s .= UtfString::code2utf(0x202B);\n            }\n        }\n\n        return $s;\n    }\n    /* -- END OTL -- */\n\n    function SetSubstitutions()\n    {\n        $subsarray = [];\n        require __DIR__ . '/../data/subs_win-1252.php';\n        $this->substitute = [];\n        foreach ($subsarray as $key => $val) {\n            $this->substitute[UtfString::code2utf($key)] = $val;\n        }\n    }\n\n    function SubstituteChars($html)\n    {\n        // only substitute characters between tags\n        if (count($this->substitute)) {\n            $a = preg_split('/(<.*?>)/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);\n            $html = '';\n            foreach ($a as $i => $e) {\n                if ($i % 2 == 0) {\n                    $e = strtr($e, $this->substitute);\n                }\n                $html .= $e;\n            }\n        }\n\n        return $html;\n    }\n\n    function SubstituteCharsSIP(&$writehtml_a, &$writehtml_i, &$writehtml_e)\n    {\n        if (preg_match(\"/^(.*?)([\\x{20000}-\\x{2FFFF}]+)(.*)/u\", $writehtml_e, $m)) {\n            if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {\n                $font = $this->CurrentFont['sipext'];\n                if (!in_array($font, $this->available_unifonts)) {\n                    return 0;\n                }\n                $writehtml_a[$writehtml_i] = $writehtml_e = $m[1];\n                array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style=\"font-family: ' . $font . '\"', $m[2], '/span', $m[3]]);\n                $this->subPos = $writehtml_i;\n                return 4;\n            }\n        }\n\n        return 0;\n    }\n\n    /**\n     * If core font is selected in document which is not onlyCoreFonts - substitute with non-core font\n     */\n    function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e)\n    {\n        // Ignore if in Textarea\n        if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {\n            return 0;\n        }\n\n        if (mb_convert_encoding(mb_convert_encoding($writehtml_e, $this->mb_enc, \"UTF-8\"), \"UTF-8\", $this->mb_enc) == $writehtml_e) {\n            return 0;\n        }\n\n        $cw = &$this->CurrentFont['cw'];\n        $unicode = $this->UTF8StringToArray($writehtml_e, false);\n        $start = -1;\n        $end = 0;\n        $flag = 0;\n        $ftype = '';\n        $u = [];\n\n        if (!$this->subArrMB) {\n\n            require __DIR__ . '/../data/subs_core.php';\n\n            $this->subArrMB['a'] = $aarr;\n            $this->subArrMB['s'] = $sarr;\n            $this->subArrMB['z'] = $zarr;\n        }\n\n        foreach ($unicode as $c => $char) {\n\n            if (($char > 127 || ($flag == 1 && $char == 32)) && $char != 173 && (!isset($this->subArrMB['a'][$char]) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {\n                if ($flag == 0) {\n                    $start = $c;\n                }\n                $flag = 1;\n                $u[] = $char;\n            } elseif ($flag > 0) {\n                $end = $c - 1;\n                break;\n            }\n        }\n\n        if ($flag > 0 && !$end) {\n            $end = count($unicode) - 1;\n        }\n\n        if ($start == -1) {\n            return 0;\n        }\n\n        // Try in backup subs font\n        if (!is_array($this->backupSubsFont)) {\n            $this->backupSubsFont = [\"$this->backupSubsFont\"];\n        }\n\n        foreach ($this->backupSubsFont as $bsfctr => $bsf) {\n\n            if ($this->fonttrans[$bsf] == 'chelvetica' || $this->fonttrans[$bsf] == 'ctimes' || $this->fonttrans[$bsf] == 'ccourier') {\n                continue;\n            }\n\n            $font = $bsf;\n            unset($cw);\n            $cw = '';\n\n            if (isset($this->fonts[$font])) {\n                $cw = &$this->fonts[$font]['cw'];\n            } elseif ($this->fontCache->has($font . '.cw.dat')) {\n                $cw = $this->fontCache->load($font . '.cw.dat');\n            } else {\n                $prevFontFamily = $this->FontFamily;\n                $prevFontStyle = $this->currentfontstyle;\n                $prevFontSizePt = $this->FontSizePt;\n                $this->SetFont($bsf, '', '', false);\n                $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);\n            }\n\n            if (!$cw) {\n                continue;\n            }\n\n            $l = 0;\n            foreach ($u as $char) {\n                if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {\n                    $l++;\n                } else {\n                    if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font\n                        $cont = mb_substr($writehtml_e, $start + 1);\n                        $writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8');\n                        array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);\n                        $this->subPos = $writehtml_i + 1;\n\n                        return 2;\n                    } else {\n                        break;\n                    }\n                }\n            }\n\n            if ($l > 0) {\n                $patt = mb_substr($writehtml_e, $start, $l, 'UTF-8');\n                if (preg_match(\"/(.*?)(\" . preg_quote($patt, '/') . \")(.*)/u\", $writehtml_e, $m)) {\n                    $writehtml_e = $m[1];\n                    array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style=\"font-family: ' . $font . '\"', $m[2], '/span', $m[3]]);\n                    $this->subPos = $writehtml_i + 3;\n\n                    return 4;\n                }\n            }\n        }\n\n        unset($cw);\n\n        return 0;\n    }\n\n    function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e)\n    {\n        // Ignore if in Textarea\n        if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {\n            return 0;\n        }\n\n        $cw = &$this->CurrentFont['cw'];\n        $unicode = $this->UTF8StringToArray($writehtml_e, false);\n        $start = -1;\n        $end = 0;\n        $flag = 0;\n        $ftype = '';\n        $u = [];\n\n        foreach ($unicode as $c => $char) {\n\n            if (($flag == 0 || $flag == 2) && (!$this->_charDefined($cw, $char) || ($flag == 2 && $char == 32)) && $this->checkSIP && $char > 131071) {  // Unicode Plane 2 (SIP)\n\n                if (in_array($this->FontFamily, $this->available_CJK_fonts)) {\n                    return 0;\n                }\n\n                if ($flag == 0) {\n                    $start = $c;\n                }\n\n                $flag = 2;\n                $u[] = $char;\n\n                // elseif (($flag == 0 || $flag==1) && $char != 173 && !$this->_charDefined($cw,$char) && ($char<1423 ||  ($char>3583 && $char < 11263))) {\n\n            } elseif (($flag == 0 || $flag == 1) && $char != 173 && (!$this->_charDefined($cw, $char) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {\n\n                if ($flag == 0) {\n                    $start = $c;\n                }\n\n                $flag = 1;\n                $u[] = $char;\n\n            } elseif ($flag > 0) {\n\n                $end = $c - 1;\n                break;\n\n            }\n        }\n\n        if ($flag > 0 && !$end) {\n            $end = count($unicode) - 1;\n        }\n\n        if ($start == -1) {\n            return 0;\n        }\n\n        if ($flag == 2) { // SIP\n\n            // Check if current CJK font has a ext-B related font\n            if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {\n                $font = $this->CurrentFont['sipext'];\n                unset($cw);\n                $cw = '';\n\n                if (isset($this->fonts[$font])) {\n                    $cw = &$this->fonts[$font]['cw'];\n                } elseif ($this->fontCache->has($font . '.cw.dat')) {\n                    $cw = $this->fontCache->load($font . '.cw.dat');\n                } else {\n                    $prevFontFamily = $this->FontFamily;\n                    $prevFontStyle = $this->currentfontstyle;\n                    $prevFontSizePt = $this->FontSizePt;\n                    $this->SetFont($font, '', '', false);\n                    $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);\n                }\n\n                if (!$cw) {\n                    return 0;\n                }\n\n                $l = 0;\n                foreach ($u as $char) {\n                    if ($this->_charDefined($cw, $char) || $char > 131071) {\n                        $l++;\n                    } else {\n                        break;\n                    }\n                }\n\n                if ($l > 0) {\n                    $patt = mb_substr($writehtml_e, $start, $l);\n                    if (preg_match(\"/(.*?)(\" . preg_quote($patt, '/') . \")(.*)/u\", $writehtml_e, $m)) {\n                        $writehtml_e = $m[1];\n                        array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style=\"font-family: ' . $font . '\"', $m[2], '/span', $m[3]]);\n                        $this->subPos = $writehtml_i + 3;\n                        return 4;\n                    }\n                }\n            }\n\n            // Check Backup SIP font (defined in Config\\FontVariables)\n            if (isset($this->backupSIPFont) && $this->backupSIPFont) {\n\n                if ($this->currentfontfamily != $this->backupSIPFont) {\n                    $font = $this->backupSIPFont;\n                } else {\n                    unset($cw);\n                    return 0;\n                }\n\n                unset($cw);\n                $cw = '';\n\n                if (isset($this->fonts[$font])) {\n                    $cw = &$this->fonts[$font]['cw'];\n                } elseif ($this->fontCache->has($font . '.cw.dat')) {\n                    $cw = $this->fontCache->load($font . '.cw.dat');\n                } else {\n                    $prevFontFamily = $this->FontFamily;\n                    $prevFontStyle = $this->currentfontstyle;\n                    $prevFontSizePt = $this->FontSizePt;\n                    $this->SetFont($this->backupSIPFont, '', '', false);\n                    $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);\n                }\n\n                if (!$cw) {\n                    return 0;\n                }\n\n                $l = 0;\n                foreach ($u as $char) {\n                    if ($this->_charDefined($cw, $char) || $char > 131071) {\n                        $l++;\n                    } else {\n                        break;\n                    }\n                }\n\n                if ($l > 0) {\n                    $patt = mb_substr($writehtml_e, $start, $l);\n                    if (preg_match(\"/(.*?)(\" . preg_quote($patt, '/') . \")(.*)/u\", $writehtml_e, $m)) {\n                        $writehtml_e = $m[1];\n                        array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style=\"font-family: ' . $font . '\"', $m[2], '/span', $m[3]]);\n                        $this->subPos = $writehtml_i + 3;\n                        return 4;\n                    }\n                }\n            }\n\n            return 0;\n        }\n\n        // FIRST TRY CORE FONTS (when appropriate)\n        if (!$this->PDFA && !$this->PDFX && !$this->biDirectional) {  // mPDF 6\n            $repl = [];\n            if (!$this->subArrMB) {\n                require __DIR__ . '/../data/subs_core.php';\n                $this->subArrMB['a'] = $aarr;\n                $this->subArrMB['s'] = $sarr;\n                $this->subArrMB['z'] = $zarr;\n            }\n            if (isset($this->subArrMB['a'][$u[0]])) {\n                $font = 'tta';\n                $ftype = 'C';\n                foreach ($u as $char) {\n                    if (isset($this->subArrMB['a'][$char])) {\n                        $repl[] = $this->subArrMB['a'][$char];\n                    } else {\n                        break;\n                    }\n                }\n            } elseif (isset($this->subArrMB['z'][$u[0]])) {\n                $font = 'ttz';\n                $ftype = 'C';\n                foreach ($u as $char) {\n                    if (isset($this->subArrMB['z'][$char])) {\n                        $repl[] = $this->subArrMB['z'][$char];\n                    } else {\n                        break;\n                    }\n                }\n            } elseif (isset($this->subArrMB['s'][$u[0]])) {\n                $font = 'tts';\n                $ftype = 'C';\n                foreach ($u as $char) {\n                    if (isset($this->subArrMB['s'][$char])) {\n                        $repl[] = $this->subArrMB['s'][$char];\n                    } else {\n                        break;\n                    }\n                }\n            }\n            if ($ftype == 'C') {\n                $patt = mb_substr($writehtml_e, $start, count($repl));\n                if (preg_match(\"/(.*?)(\" . preg_quote($patt, '/') . \")(.*)/u\", $writehtml_e, $m)) {\n                    $writehtml_e = $m[1];\n                    array_splice($writehtml_a, $writehtml_i + 1, 0, [$font, implode('|', $repl), '/' . $font, $m[3]]); // e.g. <tts>\n                    $this->subPos = $writehtml_i + 3;\n                    return 4;\n                }\n                return 0;\n            }\n        }\n\n        // LASTLY TRY IN BACKUP SUBS FONT\n        if (!is_array($this->backupSubsFont)) {\n            $this->backupSubsFont = [\"$this->backupSubsFont\"];\n        }\n\n        foreach ($this->backupSubsFont as $bsfctr => $bsf) {\n            if ($this->currentfontfamily != $bsf) {\n                $font = $bsf;\n            } else {\n                continue;\n            }\n\n            unset($cw);\n            $cw = '';\n\n            if (isset($this->fonts[$font])) {\n                $cw = &$this->fonts[$font]['cw'];\n            } elseif ($this->fontCache->has($font . '.cw.dat')) {\n                $cw = $this->fontCache->load($font . '.cw.dat');\n            } else {\n                $prevFontFamily = $this->FontFamily;\n                $prevFontStyle = $this->currentfontstyle;\n                $prevFontSizePt = $this->FontSizePt;\n                $this->SetFont($bsf, '', '', false);\n                $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);\n                if ($this->fontCache->has($font . '.cw.dat')) {\n                    $cw = $this->fontCache->load($font . '.cw.dat');\n                }\n            }\n\n            if (!$cw) {\n                continue;\n            }\n\n            $l = 0;\n            foreach ($u as $char) {\n                if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {  // Arabic and Indic\n                    $l++;\n                } else {\n                    if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font\n                        $cont = mb_substr($writehtml_e, $start + 1);\n                        $writehtml_e = mb_substr($writehtml_e, 0, $start + 1);\n                        array_splice($writehtml_a, $writehtml_i + 1, 0, ['', $cont]);\n                        $this->subPos = $writehtml_i + 1;\n                        return 2;\n                    } else {\n                        break;\n                    }\n                }\n            }\n\n            if ($l > 0) {\n                $patt = mb_substr($writehtml_e, $start, $l);\n                if (preg_match(\"/(.*?)(\" . preg_quote($patt, '/') . \")(.*)/u\", $writehtml_e, $m)) {\n                    $writehtml_e = $m[1];\n                    array_splice($writehtml_a, $writehtml_i + 1, 0, ['span style=\"font-family: ' . $font . '\"', $m[2], '/span', $m[3]]);\n                    $this->subPos = $writehtml_i + 3;\n                    return 4;\n                }\n            }\n        }\n\n        unset($cw);\n\n        return 0;\n    }\n\n    function setHiEntitySubstitutions()\n    {\n        $entarr = include __DIR__ . '/../data/entity_substitutions.php';\n\n        foreach ($entarr as $key => $val) {\n            $this->entsearch[] = '&' . $key . ';';\n            $this->entsubstitute[] = UtfString::code2utf($val);\n        }\n    }\n\n    function SubstituteHiEntities($html)\n    {\n        // converts html_entities > ASCII 127 to unicode\n        // Leaves in particular &lt; to distinguish from tag marker\n        if (count($this->entsearch)) {\n            $html = str_replace($this->entsearch, $this->entsubstitute, $html);\n        }\n\n        return $html;\n    }\n\n    /**\n     * Edited v1.2 Pass by reference; option to continue if invalid UTF-8 chars\n     */\n    function is_utf8(&$string)\n    {\n        if ($string === mb_convert_encoding(mb_convert_encoding($string, \"UTF-32\", \"UTF-8\"), \"UTF-8\", \"UTF-32\")) {\n            return true;\n        }\n\n        if ($this->ignore_invalid_utf8) {\n            $string = mb_convert_encoding(mb_convert_encoding($string, \"UTF-32\", \"UTF-8\"), \"UTF-8\", \"UTF-32\");\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * For HTML\n     *\n     * Checks string is valid UTF-8 encoded\n     * converts html_entities > ASCII 127 to UTF-8\n     * Only exception - leaves low ASCII entities e.g. &lt; &amp; etc.\n     * Leaves in particular &lt; to distinguish from tag marker\n     */\n    function purify_utf8($html, $lo = true)\n    {\n        if (!$this->is_utf8($html)) {\n\n            while (mb_convert_encoding(mb_convert_encoding($html, \"UTF-32\", \"UTF-8\"), \"UTF-8\", \"UTF-32\") != $html) {\n\n                $a = @iconv('UTF-8', 'UTF-8', $html);\n                $error = error_get_last();\n                if ($error && $error['message'] === 'iconv(): Detected an illegal character in input string') {\n                    throw new \\Mpdf\\MpdfException('Invalid input characters. Did you set $mpdf->in_charset properly?');\n                }\n\n                $pos = $start = strlen($a);\n                $err = '';\n                while (ord(substr($html, $pos, 1)) > 128) {\n                    $err .= '[[#' . ord(substr($html, $pos, 1)) . ']]';\n                    $pos++;\n                }\n\n                $this->logger->error($err, ['context' => LogContext::UTF8]);\n                $html = substr($html, $pos);\n            }\n\n            throw new \\Mpdf\\MpdfException(\"HTML contains invalid UTF-8 character(s). See log for further details\");\n        }\n\n        $html = preg_replace(\"/\\r/\", \"\", $html);\n\n        // converts html_entities > ASCII 127 to UTF-8\n        // Leaves in particular &lt; to distinguish from tag marker\n        $html = $this->SubstituteHiEntities($html);\n\n        // converts all &#nnn; or &#xHHH; to UTF-8 multibyte\n        // If $lo==true then includes ASCII < 128\n        $html = UtfString::strcode2utf($html, $lo);\n\n        return $html;\n    }\n\n    /**\n     * For TEXT\n     */\n    function purify_utf8_text($txt)\n    {\n        // Make sure UTF-8 string of characters\n        if (!$this->is_utf8($txt)) {\n            throw new \\Mpdf\\MpdfException(\"Text contains invalid UTF-8 character(s)\");\n        }\n\n        $txt = preg_replace(\"/\\r/\", \"\", $txt);\n\n        return ($txt);\n    }\n\n    function all_entities_to_utf8($txt)\n    {\n        // converts txt_entities > ASCII 127 to UTF-8\n        // Leaves in particular &lt; to distinguish from tag marker\n        $txt = $this->SubstituteHiEntities($txt);\n\n        // converts all &#nnn; or &#xHHH; to UTF-8 multibyte\n        $txt = UtfString::strcode2utf($txt);\n\n        $txt = $this->lesser_entity_decode($txt);\n        return ($txt);\n    }\n\n    /* -- BARCODES -- */\n    /**\n     * UPC/EAN barcode\n     *\n     * EAN13, EAN8, UPCA, UPCE, ISBN, ISSN\n     * Accepts 12 or 13 digits with or without - hyphens\n     */\n    function WriteBarcode($code, $showtext = 1, $x = '', $y = '', $size = 1, $border = 0, $paddingL = 1, $paddingR = 1, $paddingT = 2, $paddingB = 2, $height = 1, $bgcol = false, $col = false, $btype = 'ISBN', $supplement = '0', $supplement_code = '', $k = 1)\n    {\n        if (empty($code)) {\n            return;\n        }\n\n        $codestr = $code;\n        $code = preg_replace('/\\-/', '', $code);\n\n        $this->barcode = new Barcode();\n        if ($btype == 'ISSN' || $btype == 'ISBN') {\n            $arrcode = $this->barcode->getBarcodeArray($code, 'EAN13');\n        } else {\n            $arrcode = $this->barcode->getBarcodeArray($code, $btype);\n        }\n\n        if ($arrcode === false) {\n            throw new \\Mpdf\\MpdfException('Error in barcode string: ' . $codestr);\n        }\n\n        if ((($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') && strlen($code) === 12)\n            || ($btype == 'UPCA' && strlen($code) === 11)\n            || ($btype == 'UPCE' && strlen($code) === 11)\n            || ($btype == 'EAN8' && strlen($code) === 7)) {\n\n            $code .= $arrcode['checkdigit'];\n\n            if (stristr($codestr, '-')) {\n                $codestr .= '-' . $arrcode['checkdigit'];\n            } else {\n                $codestr .= $arrcode['checkdigit'];\n            }\n        }\n\n        if ($btype === 'ISBN') {\n            $codestr = 'ISBN ' . $codestr;\n        }\n\n        if ($btype === 'ISSN') {\n            $codestr = 'ISSN ' . $codestr;\n        }\n\n        if (empty($x)) {\n            $x = $this->x;\n        }\n\n        if (empty($y)) {\n            $y = $this->y;\n        }\n\n        // set foreground color\n        $prevDrawColor = $this->DrawColor;\n        $prevTextColor = $this->TextColor;\n        $prevFillColor = $this->FillColor;\n\n        $lw = $this->LineWidth;\n        $this->SetLineWidth(0.01);\n\n        $size /= $k; // in case resized in a table\n\n        $xres = $arrcode['nom-X'] * $size;\n        $llm = $arrcode['lightmL'] * $arrcode['nom-X'] * $size; // Left Light margin\n        $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin\n\n        $bcw = ($arrcode[\"maxw\"] * $xres); // Barcode width = Should always be 31.35mm * $size\n\n        $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins\n        $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding\n\n        $fbwi = $fbw - 2; // Full barcode width incl. light margins - 2mm - for isbn string\n        // cf. http://www.gs1uk.org/downloads/bar_code/Bar coding getting it right.pdf\n        $num_height = 3 * $size;     // Height of numerals\n        $fbh = $arrcode['nom-H'] * $size * $height;  // Full barcode height incl. numerals\n        $bch = $fbh - (1.5 * $size);     // Barcode height of bars\t (3mm for numerals)\n\n        if (($btype == 'EAN13' && $showtext) || $btype == 'ISSN' || $btype == 'ISBN') { // Add height for ISBN string + margin from top of bars\n            $tisbnm = 1.5 * $size; // Top margin between isbn (if shown) & bars\n            $codestr_fontsize = 2.1 * $size;\n            $paddingT += $codestr_fontsize + $tisbnm;\n        }\n\n        $oh = $fbh + $paddingT + $paddingB;  // Full overall height incl. user-defined padding\n\n        // PRINT border background color\n        $xpos = $x;\n        $ypos = $y;\n\n        if ($col) {\n            $this->SetDColor($col);\n            $this->SetTColor($col);\n        } else {\n            $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        }\n\n        if ($bgcol) {\n            $this->SetFColor($bgcol);\n        } else {\n            $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n        }\n\n        if (!$bgcol && !$col) { // fn. called directly - not via HTML\n\n            if ($border) {\n                $fillb = 'DF';\n            } else {\n                $fillb = 'F';\n            }\n\n            $this->Rect($xpos, $ypos, $ow, $oh, $fillb);\n        }\n\n\n        // PRINT BARS\n        $xpos = $x + $paddingL + $llm;\n        $ypos = $y + $paddingT;\n\n        if ($col) {\n            $this->SetFColor($col);\n        } else {\n            $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        }\n\n        if ($arrcode !== false) {\n            foreach ($arrcode[\"bcode\"] as $v) {\n                $bw = ($v[\"w\"] * $xres);\n                if ($v[\"t\"]) {\n                    // draw a vertical bar\n                    $this->Rect($xpos, $ypos, $bw, $bch, 'F');\n                }\n                $xpos += $bw;\n            }\n        }\n\n        // print text\n        $prevFontFamily = $this->FontFamily;\n        $prevFontStyle = $this->FontStyle;\n        $prevFontSizePt = $this->FontSizePt;\n\n        // ISBN string\n        if (($btype === 'EAN13' && $showtext) || $btype === 'ISBN' || $btype === 'ISSN') {\n\n            if ($this->onlyCoreFonts) {\n                $this->SetFont('chelvetica');\n            } else {\n                $this->SetFont('sans');\n            }\n\n            if ($bgcol) {\n                $this->SetFColor($bgcol);\n            } else {\n                $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n            }\n\n            $this->x = $x + $paddingL + 1; // 1mm left margin (cf. $fbwi above)\n\n            // max width is $fbwi\n            $loop = 0;\n            while ($loop == 0) {\n                $this->SetFontSize($codestr_fontsize * 1.4 * Mpdf::SCALE, false); // don't write\n                $sz = $this->GetStringWidth($codestr);\n\n                if ($sz > $fbwi) {\n                    $codestr_fontsize -= 0.1;\n                } else {\n                    $loop ++;\n                }\n            }\n\n            $this->SetFont('', '', $codestr_fontsize * 1.4 * Mpdf::SCALE, true, true); // * 1.4 because font height is only 7/10 of given mm\n            // WORD SPACING\n            if ($fbwi > $sz) {\n                $xtra = $fbwi - $sz;\n                $charspacing = $xtra / (strlen($codestr) - 1);\n                if ($charspacing) {\n                    $this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));\n                }\n            }\n\n            $this->y = $y + $paddingT - ($codestr_fontsize ) - $tisbnm;\n            $this->Cell($fbw, $codestr_fontsize, $codestr);\n\n            if ($charspacing) {\n                $this->writer->write('BT 0 Tc ET');\n            }\n        }\n\n\n        // Bottom NUMERALS\n        // mPDF 5.7.4\n        if ($this->onlyCoreFonts) {\n            $this->SetFont('ccourier');\n            $fh = 1.3;\n        } else {\n            $this->SetFont('ocrb');\n            $fh = 1.06;\n        }\n\n        $charRO = '';\n\n        if ($btype === 'EAN13' || $btype === 'ISBN' || $btype === 'ISSN') {\n\n            $outerfontsize = 3; // Inner fontsize = 3\n            $outerp = $xres * 4;\n            $innerp = $xres * 2.5;\n            $textw = ($bcw * 0.5) - $outerp - $innerp;\n            $chars = 6; // number of numerals in each half\n            $charLO = substr($code, 0, 1); // Left Outer\n            $charLI = substr($code, 1, 6); // Left Inner\n            $charRI = substr($code, 7, 6); // Right Inner\n\n            if (!$supplement) {\n                $charRO = '>'; // Right Outer\n            }\n\n        } elseif ($btype === 'UPCA') {\n\n            $outerfontsize = 2.3; // Inner fontsize = 3\n            $outerp = $xres * 10;\n            $innerp = $xres * 2.5;\n            $textw = ($bcw * 0.5) - $outerp - $innerp;\n            $chars = 5;\n            $charLO = substr($code, 0, 1); // Left Outer\n            $charLI = substr($code, 1, 5); // Left Inner\n            $charRI = substr($code, 6, 5); // Right Inner\n            $charRO = substr($code, 11, 1); // Right Outer\n\n        } elseif ($btype === 'UPCE') {\n\n            $outerfontsize = 2.3; // Inner fontsize = 3\n            $outerp = $xres * 4;\n            $innerp = 0;\n            $textw = ($bcw * 0.5) - $outerp - $innerp;\n            $chars = 3;\n            $upce_code = $arrcode['code'];\n            $charLO = substr($code, 0, 1); // Left Outer\n            $charLI = substr($upce_code, 0, 3); // Left Inner\n            $charRI = substr($upce_code, 3, 3); // Right Inner\n            $charRO = substr($code, 11, 1); // Right Outer\n\n        } elseif ($btype === 'EAN8') {\n\n            $outerfontsize = 3; // Inner fontsize = 3\n            $outerp = $xres * 4;\n            $innerp = $xres * 2.5;\n            $textw = ($bcw * 0.5) - $outerp - $innerp;\n            $chars = 4;\n            $charLO = '<'; // Left Outer\n            $charLI = substr($code, 0, 4); // Left Inner\n            $charRI = substr($code, 4, 4); // Right Inner\n\n            if (!$supplement) {\n                $charRO = '>'; // Right Outer\n            }\n        }\n\n        $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)\n\n        if (!$this->usingCoreFont) { // character width at 3mm\n            $cw = $this->_getCharWidth($this->CurrentFont['cw'], 32) * 3 * $fh * $size / 1000;\n        } else {\n            $cw = 600 * 3 * $fh * $size / 1000;\n        }\n\n        // Outer left character\n        $y_text = $y + $paddingT + $bch - ($num_height / 2);\n        $y_text_outer = $y + $paddingT + $bch - ($num_height * ($outerfontsize / 3) / 2);\n\n        $this->x = $x + $paddingL - ($cw * ($outerfontsize / 3) * 0.1); // 0.1 is correction as char does not fill full width;\n        $this->y = $y_text_outer;\n        $this->Cell($cw, $num_height, $charLO);\n\n        // WORD SPACING for inner chars\n        $xtra = $textw - ($cw * $chars);\n        $charspacing = $xtra / ($chars - 1);\n        if ($charspacing) {\n            $this->writer->write(sprintf('BT %.3F Tc ET', $charspacing * Mpdf::SCALE));\n        }\n\n        if ($bgcol) {\n            $this->SetFColor($bgcol);\n        } else {\n            $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n        }\n\n        $this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)\n\n        // Inner left half characters\n        $this->x = $x + $paddingL + $llm + $outerp;\n        $this->y = $y_text;\n        $this->Cell($textw, $num_height, $charLI, 0, 0, '', 1);\n\n        // Inner right half characters\n        $this->x = $x + $paddingL + $llm + ($bcw * 0.5) + $innerp;\n        $this->y = $y_text;\n        $this->Cell($textw, $num_height, $charRI, 0, 0, '', 1);\n\n        if ($charspacing) {\n            $this->writer->write('BT 0 Tc ET');\n        }\n\n        // Outer Right character\n        $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)\n\n        $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * ($outerfontsize / 3) * 0.9); // 0.9 is correction as char does not fill full width\n        $this->y = $y_text_outer;\n        $this->Cell($cw * ($outerfontsize / 3), $num_height, $charRO, 0, 0, 'R');\n\n        if ($supplement) { // EAN-2 or -5 Supplement\n            // PRINT BARS\n            $supparrcode = $this->barcode->getBarcodeArray($supplement_code, 'EAN' . $supplement);\n\n            if ($supparrcode === false) {\n                throw new \\Mpdf\\MpdfException('Error in barcode string (supplement): ' . $codestr . ' ' . $supplement_code);\n            }\n\n            if (strlen($supplement_code) != $supplement) {\n                throw new \\Mpdf\\MpdfException('Barcode supplement incorrect: ' . $supplement_code);\n            }\n\n            $llm = $fbw - (($arrcode['lightmR'] - $supparrcode['sepM']) * $arrcode['nom-X'] * $size); // Left Light margin\n            $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin\n\n            $bcw = ($supparrcode[\"maxw\"] * $xres); // Barcode width = Should always be 31.35mm * $size\n\n            $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins\n            $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding\n            $bch = $fbh - (1.5 * $size) - ($num_height + 0.5);  // Barcode height of bars\t (3mm for numerals)\n\n            $xpos = $x + $paddingL + $llm;\n            $ypos = $y + $paddingT + $num_height + 0.5;\n\n            if ($col) {\n                $this->SetFColor($col);\n            } else {\n                $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            }\n\n            if ($supparrcode !== false) {\n                foreach ($supparrcode[\"bcode\"] as $v) {\n                    $bw = ($v[\"w\"] * $xres);\n                    if ($v[\"t\"]) {\n                        // draw a vertical bar\n                        $this->Rect($xpos, $ypos, $bw, $bch, 'F');\n                    }\n                    $xpos += $bw;\n                }\n            }\n\n            // Characters\n            if ($bgcol) {\n                $this->SetFColor($bgcol);\n            } else {\n                $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n            }\n\n            $this->SetFontSize(3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)\n            $this->x = $x + $paddingL + $llm;\n            $this->y = $y + $paddingT;\n            $this->Cell($bcw, $num_height, $supplement_code, 0, 0, 'C');\n\n            // Outer Right character (light margin)\n            $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * Mpdf::SCALE); // 3mm numerals (FontSize is larger to account for space above/below characters)\n            $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * 0.9); // 0.9 is correction as char does not fill full width\n            $this->y = $y + $paddingT;\n            $this->Cell($cw * ($outerfontsize / 3), $num_height, '>', 0, 0, 'R');\n        }\n\n        // Restore **************\n        $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);\n        $this->DrawColor = $prevDrawColor;\n        $this->TextColor = $prevTextColor;\n        $this->FillColor = $prevFillColor;\n        $this->SetLineWidth($lw);\n        $this->SetY($y);\n    }\n\n    /**\n     * POSTAL and OTHER barcodes\n     */\n    function WriteBarcode2($code, $x = '', $y = '', $size = 1, $height = 1, $bgcol = false, $col = false, $btype = 'IMB', $print_ratio = '', $k = 1)\n    {\n        if (empty($code)) {\n            return;\n        }\n\n        $this->barcode = new Barcode();\n        $arrcode = $this->barcode->getBarcodeArray($code, $btype, $print_ratio);\n\n        if (empty($x)) {\n            $x = $this->x;\n        }\n\n        if (empty($y)) {\n            $y = $this->y;\n        }\n\n        $prevDrawColor = $this->DrawColor;\n        $prevTextColor = $this->TextColor;\n        $prevFillColor = $this->FillColor;\n        $lw = $this->LineWidth;\n        $this->SetLineWidth(0.01);\n        $size /= $k; // in case resized in a table\n        $xres = $arrcode['nom-X'] * $size;\n\n        if ($btype === 'IMB' || $btype === 'RM4SCC' || $btype === 'KIX' || $btype === 'POSTNET' || $btype === 'PLANET') {\n            $llm = $arrcode['quietL'] / $k; // Left Quiet margin\n            $rlm = $arrcode['quietR'] / $k; // Right Quiet margin\n            $tlm = $blm = $arrcode['quietTB'] / $k;\n            $height = 1;  // Overrides\n        } elseif (in_array($btype, ['C128A', 'C128B', 'C128C', 'C128RAW', 'EAN128A', 'EAN128B', 'EAN128C', 'C39', 'C39+', 'C39E', 'C39E+', 'S25', 'S25+', 'I25', 'I25+', 'I25B', 'I25B+', 'C93', 'MSI', 'MSI+', 'CODABAR', 'CODE11'])) {\n            $llm = $arrcode['lightmL'] * $xres; // Left Quiet margin\n            $rlm = $arrcode['lightmR'] * $xres; // Right Quiet margin\n            $tlm = $blm = $arrcode['lightTB'] * $xres * $height;\n        }\n\n        $bcw = ($arrcode[\"maxw\"] * $xres);\n        $fbw = $bcw + $llm + $rlm;  // Full barcode width incl. light margins\n\n        $bch = ($arrcode[\"nom-H\"] * $size * $height);\n        $fbh = $bch + $tlm + $blm;  // Full barcode height\n\n        // PRINT border background color\n        $xpos = $x;\n        $ypos = $y;\n\n        if ($col) {\n            $this->SetDColor($col);\n            $this->SetTColor($col);\n        } else {\n            $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n            $this->SetTColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        }\n\n        if ($bgcol) {\n            $this->SetFColor($bgcol);\n        } else {\n            $this->SetFColor($this->colorConverter->convert(255, $this->PDFAXwarnings));\n        }\n\n        // PRINT BARS\n        if ($col) {\n            $this->SetFColor($col);\n        } else {\n            $this->SetFColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        }\n        $xpos = $x + $llm;\n\n        if ($arrcode !== false) {\n            foreach ($arrcode[\"bcode\"] as $v) {\n                $bw = ($v[\"w\"] * $xres);\n                if ($v[\"t\"]) {\n                    $ypos = $y + $tlm + ($bch * $v['p'] / $arrcode['maxh']);\n                    $this->Rect($xpos, $ypos, $bw, ($v['h'] * $bch / $arrcode['maxh']), 'F');\n                }\n                $xpos += $bw;\n            }\n        }\n\n        // PRINT BEARER BARS\n        if ($btype == 'I25B' || $btype == 'I25B+') {\n            $this->Rect($x, $y, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');\n            $this->Rect($x, $y + $tlm + $bch, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');\n        }\n\n        // Restore **************\n        $this->DrawColor = $prevDrawColor;\n        $this->TextColor = $prevTextColor;\n        $this->FillColor = $prevFillColor;\n        $this->SetLineWidth($lw);\n        $this->SetY($y);\n    }\n    /* -- END BARCODES -- */\n\n    function StartTransform($returnstring = false)\n    {\n        if ($returnstring) {\n            return('q');\n        } else {\n            $this->writer->write('q');\n        }\n    }\n\n    function StopTransform($returnstring = false)\n    {\n        if ($returnstring) {\n            return('Q');\n        } else {\n            $this->writer->write('Q');\n        }\n    }\n\n    function transformScale($s_x, $s_y, $x = '', $y = '', $returnstring = false)\n    {\n        if ($x === '') {\n            $x = $this->x;\n        }\n\n        if ($y === '') {\n            $y = $this->y;\n        }\n\n        if (($s_x == 0) or ( $s_y == 0)) {\n            throw new \\Mpdf\\MpdfException('Please do not use values equal to zero for scaling');\n        }\n\n        $y = ($this->h - $y) * Mpdf::SCALE;\n        $x *= Mpdf::SCALE;\n\n        // calculate elements of transformation matrix\n        $s_x /= 100;\n        $s_y /= 100;\n        $tm = [];\n        $tm[0] = $s_x;\n        $tm[1] = 0;\n        $tm[2] = 0;\n        $tm[3] = $s_y;\n        $tm[4] = $x * (1 - $s_x);\n        $tm[5] = $y * (1 - $s_y);\n\n        // scale the coordinate system\n        if ($returnstring) {\n            return($this->_transform($tm, true));\n        } else {\n            $this->_transform($tm);\n        }\n    }\n\n    function transformTranslate($t_x, $t_y, $returnstring = false)\n    {\n        // calculate elements of transformation matrix\n        $tm = [];\n        $tm[0] = 1;\n        $tm[1] = 0;\n        $tm[2] = 0;\n        $tm[3] = 1;\n        $tm[4] = $t_x * Mpdf::SCALE;\n        $tm[5] = -$t_y * Mpdf::SCALE;\n\n        // translate the coordinate system\n        if ($returnstring) {\n            return($this->_transform($tm, true));\n        } else {\n            $this->_transform($tm);\n        }\n    }\n\n    function transformRotate($angle, $x = '', $y = '', $returnstring = false)\n    {\n        if ($x === '') {\n            $x = $this->x;\n        }\n\n        if ($y === '') {\n            $y = $this->y;\n        }\n\n        $angle = -$angle;\n        $y = ($this->h - $y) * Mpdf::SCALE;\n        $x *= Mpdf::SCALE;\n\n        // calculate elements of transformation matrix\n        $tm = [];\n        $tm[0] = cos(deg2rad($angle));\n        $tm[1] = sin(deg2rad($angle));\n        $tm[2] = -$tm[1];\n        $tm[3] = $tm[0];\n        $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;\n        $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;\n\n        // rotate the coordinate system around ($x,$y)\n        if ($returnstring) {\n            return $this->_transform($tm, true);\n        } else {\n            $this->_transform($tm);\n        }\n    }\n\n    /**\n     * mPDF 5.7.3 TRANSFORMS\n     */\n    function transformSkew($angle_x, $angle_y, $x = '', $y = '', $returnstring = false)\n    {\n        if ($x === '') {\n            $x = $this->x;\n        }\n\n        if ($y === '') {\n            $y = $this->y;\n        }\n\n        $angle_x = -$angle_x;\n        $angle_y = -$angle_y;\n\n        $x *= Mpdf::SCALE;\n        $y = ($this->h - $y) * Mpdf::SCALE;\n\n        // calculate elements of transformation matrix\n        $tm = [];\n        $tm[0] = 1;\n        $tm[1] = tan(deg2rad($angle_y));\n        $tm[2] = tan(deg2rad($angle_x));\n        $tm[3] = 1;\n        $tm[4] = -$tm[2] * $y;\n        $tm[5] = -$tm[1] * $x;\n\n        // skew the coordinate system\n        if ($returnstring) {\n            return $this->_transform($tm, true);\n        } else {\n            $this->_transform($tm);\n        }\n    }\n\n    function _transform($tm, $returnstring = false)\n    {\n        if ($returnstring) {\n            return(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));\n        } else {\n            $this->writer->write(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));\n        }\n    }\n\n    // AUTOFONT =========================\n    function markScriptToLang($html)\n    {\n        if ($this->onlyCoreFonts) {\n            return $html;\n        }\n\n        $n = '';\n        $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);\n        foreach ($a as $i => $e) {\n            if ($i % 2 == 0) {\n\n                // ignore if in Textarea\n                if ($i > 0 && strtolower(substr($a[$i - 1], 1, 8)) == 'textarea') {\n                    $a[$i] = $e;\n                    continue;\n                }\n\n                $e = UtfString::strcode2utf($e);\n                $e = $this->lesser_entity_decode($e);\n\n                $earr = $this->UTF8StringToArray($e, false);\n\n                $scriptblock = 0;\n                $scriptblocks = [];\n                $scriptblocks[0] = 0;\n                $chardata = [];\n                $subchunk = 0;\n                $charctr = 0;\n\n                foreach ($earr as $char) {\n\n                    $ucd_record = Ucdn::get_ucd_record($char);\n                    $sbl = $ucd_record[6];\n\n                    if ($sbl && $sbl != 40 && $sbl != 102) {\n                        if ($scriptblock == 0) {\n                            $scriptblock = $sbl;\n                            $scriptblocks[$subchunk] = $scriptblock;\n                        } elseif ($scriptblock > 0 && $scriptblock != $sbl) {\n                            // NEW (non-common) Script encountered in this chunk.\n                            // Start a new subchunk\n                            $subchunk++;\n                            $scriptblock = $sbl;\n                            $charctr = 0;\n                            $scriptblocks[$subchunk] = $scriptblock;\n                        }\n                    }\n\n                    $chardata[$subchunk][$charctr]['script'] = $sbl;\n                    $chardata[$subchunk][$charctr]['uni'] = $char;\n                    $charctr++;\n                }\n\n                // If scriptblock[x] = common & non-baseScript\n                // and scriptblock[x+1] = baseScript\n                // Move common script from end of x to start of x+1\n                for ($sch = 0; $sch < $subchunk; $sch++) {\n                    if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $scriptblocks[$sch + 1] == $this->baseScript) {\n                        $end = count($chardata[$sch]) - 1;\n                        while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script\n                            $tmp = array_pop($chardata[$sch]);\n                            array_unshift($chardata[$sch + 1], $tmp);\n                            $end--;\n                        }\n                    }\n                }\n\n                $o = '';\n                for ($sch = 0; $sch <= $subchunk; $sch++) {\n\n                    if (isset($chardata[$sch])) {\n                        $s = '';\n                        for ($j = 0; $j < count($chardata[$sch]); $j++) {\n                            $s .= UtfString::code2utf($chardata[$sch][$j]['uni']);\n                        }\n\n                        // ZZZ99 Undo lesser_entity_decode as above - but only for <>&\n                        $s = str_replace(\"&\", \"&amp;\", $s);\n                        $s = str_replace(\"<\", \"&lt;\", $s);\n                        $s = str_replace(\">\", \"&gt;\", $s);\n\n                        // Check Vietnamese if Latin script - even if Basescript\n                        if ($scriptblocks[$sch] == Ucdn::SCRIPT_LATIN && $this->autoVietnamese && preg_match(\"/([\" . $this->scriptToLanguage->getLanguageDelimiters('viet') . \"])/u\", $s)) {\n                            $o .= '<span lang=\"vi\" class=\"lang_vi\">' . $s . '</span>';\n                        } elseif ($scriptblocks[$sch] == Ucdn::SCRIPT_ARABIC && $this->autoArabic) { // Check Arabic for different languages if Arabic script - even if Basescript\n                            if (preg_match(\"/[\" . $this->scriptToLanguage->getLanguageDelimiters('sindhi') . \"]/u\", $s)) {\n                                $o .= '<span lang=\"sd\" class=\"lang_sd\">' . $s . '</span>';\n                            } elseif (preg_match(\"/[\" . $this->scriptToLanguage->getLanguageDelimiters('urdu') . \"]/u\", $s)) {\n                                $o .= '<span lang=\"ur\" class=\"lang_ur\">' . $s . '</span>';\n                            } elseif (preg_match(\"/[\" . $this->scriptToLanguage->getLanguageDelimiters('pashto') . \"]/u\", $s)) {\n                                $o .= '<span lang=\"ps\" class=\"lang_ps\">' . $s . '</span>';\n                            } elseif (preg_match(\"/[\" . $this->scriptToLanguage->getLanguageDelimiters('persian') . \"]/u\", $s)) {\n                                $o .= '<span lang=\"fa\" class=\"lang_fa\">' . $s . '</span>';\n                            } elseif ($this->baseScript != Ucdn::SCRIPT_ARABIC && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) {\n                                $o .= '<span lang=\"' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '\" class=\"lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '\">' . $s . '</span>';\n                            } else {\n                                // Just output chars\n                                $o .= $s;\n                            }\n                        } elseif ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch])) { // Identify Script block if not Basescript, and mark up as language\n                            // Encase in <span>\n                            $o .= '<span lang=\"' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '\" class=\"lang_' . $this->scriptToLanguage->getLanguageByScript($scriptblocks[$sch]) . '\">';\n                            $o .= $s;\n                            $o .= '</span>';\n                        } else {\n                            // Just output chars\n                            $o .= $s;\n                        }\n                    }\n                }\n\n                $a[$i] = $o;\n            } else {\n                $a[$i] = '<' . $e . '>';\n            }\n        }\n\n        $n = implode('', $a);\n\n        return $n;\n    }\n\n    /* -- COLUMNS -- */\n    /**\n     * Callback function from function printcolumnbuffer in mpdf\n     */\n    function columnAdjustAdd($type, $k, $xadj, $yadj, $a, $b, $c = 0, $d = 0, $e = 0, $f = 0)\n    {\n        if ($type === 'Td') {  // xpos,ypos\n\n            $a += ($xadj * $k);\n            $b -= ($yadj * $k);\n\n            return 'BT ' . sprintf('%.3F %.3F', $a, $b) . ' Td';\n\n        } elseif ($type === 're') {  // xpos,ypos,width,height\n\n            $a += ($xadj * $k);\n            $b -= ($yadj * $k);\n\n            return sprintf('%.3F %.3F %.3F %.3F', $a, $b, $c, $d) . ' re';\n\n        } elseif ($type === 'l') {  // xpos,ypos,x2pos,y2pos\n\n            $a += ($xadj * $k);\n            $b -= ($yadj * $k);\n\n            return sprintf('%.3F %.3F l', $a, $b);\n\n        } elseif ($type === 'img') {  // width,height,xpos,ypos\n\n            $c += ($xadj * $k);\n            $d -= ($yadj * $k);\n\n            return sprintf('q %.3F 0 0 %.3F %.3F %.3F', $a, $b, $c, $d) . ' cm /' . $e;\n\n        } elseif ($type === 'draw') {  // xpos,ypos\n\n            $a += ($xadj * $k);\n            $b -= ($yadj * $k);\n\n            return sprintf('%.3F %.3F m', $a, $b);\n\n        } elseif ($type === 'bezier') {  // xpos,ypos,x2pos,y2pos,x3pos,y3pos\n\n            $a += ($xadj * $k);\n            $b -= ($yadj * $k);\n            $c += ($xadj * $k);\n            $d -= ($yadj * $k);\n            $e += ($xadj * $k);\n            $f -= ($yadj * $k);\n\n            return sprintf('%.3F %.3F %.3F %.3F %.3F %.3F', $a, $b, $c, $d, $e, $f) . ' c';\n        }\n    }\n\n    /* -- END COLUMNS -- */\n\n    // mPDF 5.7.3 TRANSFORMS\n    function ConvertAngle($s, $makepositive = true)\n    {\n        if (preg_match('/([\\-]*[0-9\\.]+)(deg|grad|rad)/i', $s, $m)) {\n\n            $angle = $m[1] + 0;\n\n            if (strtolower($m[2]) == 'deg') {\n                $angle = $angle;\n            } elseif (strtolower($m[2]) == 'grad') {\n                $angle *= (360 / 400);\n            } elseif (strtolower($m[2]) == 'rad') {\n                $angle = rad2deg($angle);\n            }\n\n            while ($angle >= 360) {\n                $angle -= 360;\n            }\n\n            while ($angle <= -360) {\n                $angle += 360;\n            }\n\n            if ($makepositive) { // always returns an angle between 0 and 360deg\n                if ($angle < 0) {\n                    $angle += 360;\n                }\n            }\n\n        } else {\n            $angle = $s + 0;\n        }\n\n        return $angle;\n    }\n\n    function lesser_entity_decode($html)\n    {\n        // supports the most used entity codes (only does ascii safe characters)\n        $html = str_replace(\"&lt;\", \"<\", $html);\n        $html = str_replace(\"&gt;\", \">\", $html);\n\n        $html = str_replace(\"&apos;\", \"'\", $html);\n        $html = str_replace(\"&quot;\", '\"', $html);\n        $html = str_replace(\"&amp;\", \"&\", $html);\n\n        return $html;\n    }\n\n    function AdjustHTML($html, $tabSpaces = 8)\n    {\n        $limit = ini_get('pcre.backtrack_limit');\n        if (strlen($html) > $limit) {\n            throw new \\Mpdf\\MpdfException(sprintf(\n                'The HTML code size is larger than pcre.backtrack_limit %d. You should use WriteHTML() with smaller string lengths.',\n                $limit\n            ));\n        }\n\n        preg_match_all(\"/(<annotation.*?>)/si\", $html, $m);\n        if (count($m[1])) {\n            for ($i = 0; $i < count($m[1]); $i++) {\n                $sub = preg_replace(\"/\\n/si\", \"\\xbb\\xa4\\xac\", $m[1][$i]);\n                $html = preg_replace('/' . preg_quote($m[1][$i], '/') . '/si', $sub, $html);\n            }\n        }\n\n        preg_match_all(\"/(<svg.*?<\\/svg>)/si\", $html, $svgi);\n        if (count($svgi[0])) {\n            for ($i = 0; $i < count($svgi[0]); $i++) {\n                $file = $this->cache->write('/_tempSVG' . uniqid(random_int(1, 100000), true) . '_' . $i . '.svg', $svgi[0][$i]);\n                $html = str_replace($svgi[0][$i], '<img src=\"' . $file . '\" />', $html);\n            }\n        }\n\n        // Remove javascript code from HTML (should not appear in the PDF file)\n        $html = preg_replace('/<script.*?<\\/script>/is', '', $html);\n\n        // Remove special comments\n        $html = preg_replace('/<!--mpdf/i', '', $html);\n        $html = preg_replace('/mpdf-->/i', '', $html);\n\n        // Remove comments from HTML (should not appear in the PDF file)\n        $html = preg_replace('/<!--.*?-->/s', '', $html);\n\n        $html = preg_replace('/\\f/', '', $html); // replace formfeed by nothing\n        $html = preg_replace('/\\r/', '', $html); // replace carriage return by nothing\n\n        // Well formed XHTML end tags\n        $html = preg_replace('/<(br|hr)>/i', \"<\\\\1 />\", $html); // mPDF 6\n        $html = preg_replace('/<(br|hr)\\/>/i', \"<\\\\1 />\", $html);\n\n        // Get rid of empty <thead></thead> etc\n        $html = preg_replace('/<tr>\\s*<\\/tr>/i', '', $html);\n        $html = preg_replace('/<thead>\\s*<\\/thead>/i', '', $html);\n        $html = preg_replace('/<tfoot>\\s*<\\/tfoot>/i', '', $html);\n        $html = preg_replace('/<table[^>]*>\\s*<\\/table>/i', '', $html);\n\n        // Remove spaces at end of table cells\n        $html = preg_replace(\"/[ \\n\\r]+<\\/t(d|h)/\", '</t\\\\1', $html);\n\n        $html = preg_replace(\"/[ ]*<dottab\\s*[\\/]*>[ ]*/\", '<dottab />', $html);\n\n        // Concatenates any Substitute characters from symbols/dingbats\n        $html = str_replace('</tts><tts>', '|', $html);\n        $html = str_replace('</ttz><ttz>', '|', $html);\n        $html = str_replace('</tta><tta>', '|', $html);\n\n        $html = preg_replace('/<br \\/>\\s*/is', \"<br />\", $html);\n\n        $html = preg_replace('/<wbr[ \\/]*>\\s*/is', \"&#173;\", $html);\n\n        // Preserve '\\n's in content between the tags <pre> and </pre>\n        if (preg_match('/<pre/', $html)) {\n\n            $html_a = preg_split('/(\\<\\/?pre[^\\>]*\\>)/', $html, -1, 2);\n            $h = [];\n            $c = 0;\n\n            foreach ($html_a as $s) {\n                if ($c > 1 && preg_match('/^<\\/pre/i', $s)) {\n                    $c--;\n                    $s = preg_replace('/<\\/pre/i', '</innerpre', $s);\n                } elseif ($c > 0 && preg_match('/^<pre/i', $s)) {\n                    $c++;\n                    $s = preg_replace('/<pre/i', '<innerpre', $s);\n                } elseif (preg_match('/^<pre/i', $s)) {\n                    $c++;\n                } elseif (preg_match('/^<\\/pre/i', $s)) {\n                    $c--;\n                }\n                array_push($h, $s);\n            }\n\n            $html = implode('', $h);\n        }\n\n        $thereispre = preg_match_all('#<pre(.*?)>(.*?)</pre>#si', $html, $temp);\n\n        // Preserve '\\n's in content between the tags <textarea> and </textarea>\n        $thereistextarea = preg_match_all('#<textarea(.*?)>(.*?)</textarea>#si', $html, $temp2);\n        $html = preg_replace('/[\\n]/', ' ', $html); // replace linefeed by spaces\n        $html = preg_replace('/[\\t]/', ' ', $html); // replace tabs by spaces\n\n        // Converts < to &lt; when not a tag\n        $html = preg_replace('/<([^!\\/a-zA-Z_:])/i', '&lt;\\\\1', $html); // mPDF 5.7.3\n        $html = preg_replace(\"/[ ]+/\", ' ', $html);\n\n        $html = preg_replace('/\\/li>\\s+<\\/(u|o)l/i', '/li></\\\\1l', $html);\n        $html = preg_replace('/\\/(u|o)l>\\s+<\\/li/i', '/\\\\1l></li', $html);\n        $html = preg_replace('/\\/li>\\s+<\\/(u|o)l/i', '/li></\\\\1l', $html);\n        $html = preg_replace('/\\/li>\\s+<li/i', '/li><li', $html);\n        $html = preg_replace('/<(u|o)l([^>]*)>[ ]+/i', '<\\\\1l\\\\2>', $html);\n        $html = preg_replace('/[ ]+<(u|o)l/i', '<\\\\1l', $html);\n\n        // Make self closing tabs valid XHTML\n        // Tags which are self-closing: 1) Replaceable and 2) Non-replaced items\n        $selftabs = 'input|hr|img|br|barcode|dottab';\n        $selftabs2 = 'indexentry|indexinsert|bookmark|watermarktext|watermarkimage|column_break|columnbreak|newcolumn|newpage|page_break|pagebreak|formfeed|columns|toc|tocpagebreak|setpageheader|setpagefooter|sethtmlpageheader|sethtmlpagefooter|annotation';\n\n        // Fix self-closing tags which don't close themselves\n        $html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . ')[^>\\/]*)>/i', '\\\\1 />', $html);\n\n        // Fix self-closing tags that don't include a space between the tag name and the closing slash\n        $html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . '))\\/>/i', '\\\\1 />', $html);\n\n        $iterator = 0;\n        while ($thereispre) { // Recover <pre attributes>content</pre>\n            $temp[2][$iterator] = preg_replace('/<([^!\\/a-zA-Z_:])/', '&lt;\\\\1', $temp[2][$iterator]); // mPDF 5.7.2\t// mPDF 5.7.3\n\n            $temp[2][$iterator] = preg_replace_callback(\"/^([^\\n\\t]*?)\\t/m\", [$this, 'tabs2spaces_callback'], $temp[2][$iterator]); // mPDF 5.7+\n            $temp[2][$iterator] = preg_replace('/\\t/', str_repeat(\" \", $tabSpaces), $temp[2][$iterator]);\n\n            $temp[2][$iterator] = preg_replace('/\\n/', \"<br />\", $temp[2][$iterator]);\n            $temp[2][$iterator] = str_replace('\\\\', \"\\\\\\\\\", $temp[2][$iterator]);\n            // $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1);\n            $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si', '<erp' . $temp[1][$iterator] . '>' . str_replace('$', '\\$', $temp[2][$iterator]) . '</erp>', $html, 1); // mPDF 5.7+\n            $thereispre--;\n            $iterator++;\n        }\n\n        $iterator = 0;\n        while ($thereistextarea) { // Recover <textarea attributes>content</textarea>\n            $temp2[2][$iterator] = preg_replace('/\\t/', str_repeat(\" \", $tabSpaces), $temp2[2][$iterator]);\n            $temp2[2][$iterator] = str_replace('\\\\', \"\\\\\\\\\", $temp2[2][$iterator]);\n            $html = preg_replace('#<textarea(.*?)>(.*?)</textarea>#si', '<aeratxet' . $temp2[1][$iterator] . '>' . trim($temp2[2][$iterator]) . '</aeratxet>', $html, 1);\n            $thereistextarea--;\n            $iterator++;\n        }\n\n        // Restore original tag names\n        $html = str_replace(\"<erp\", \"<pre\", $html);\n        $html = str_replace(\"</erp>\", \"</pre>\", $html);\n        $html = str_replace(\"<aeratxet\", \"<textarea\", $html);\n        $html = str_replace(\"</aeratxet>\", \"</textarea>\", $html);\n        $html = str_replace(\"</innerpre\", \"</pre\", $html);\n        $html = str_replace(\"<innerpre\", \"<pre\", $html);\n\n        $html = preg_replace('/<textarea([^>]*)><\\/textarea>/si', '<textarea\\\\1> </textarea>', $html);\n        $html = preg_replace('/(<table[^>]*>)\\s*(<caption)(.*?<\\/caption>)(.*?<\\/table>)/si', '\\\\2 position=\"top\"\\\\3\\\\1\\\\4\\\\2 position=\"bottom\"\\\\3', $html); // *TABLES*\n        $html = preg_replace('/<(h[1-6])([^>]*)(>(?:(?!h[1-6]).)*?<\\/\\\\1>\\s*<table)/si', '<\\\\1\\\\2 keep-with-table=\"1\"\\\\3', $html); // *TABLES*\n        $html = preg_replace(\"/\\xbb\\xa4\\xac/\", \"\\n\", $html);\n\n        // Fixes <p>&#8377</p> which browser copes with even though it is wrong!\n        $html = preg_replace(\"/(&#[x]{0,1}[0-9a-f]{1,5})</i\", \"\\\\1;<\", $html);\n\n        return $html;\n    }\n\n    // mPDF 5.7+\n    function tabs2spaces_callback($matches)\n    {\n        return (stripslashes($matches[1]) . str_repeat(' ', $this->tabSpaces - (mb_strlen(stripslashes($matches[1])) % $this->tabSpaces)));\n    }\n\n    // mPDF 5.7+\n    function date_callback($matches)\n    {\n        return date($matches[1]);\n    }\n\n    // ========== OVERWRITE SEARCH STRING IN A PDF FILE ================\n    function OverWrite($file_in, $search, $replacement, $dest = Destination::DOWNLOAD, $file_out = \"mpdf\")\n    {\n        $pdf = file_get_contents($file_in);\n\n        if (!is_array($search)) {\n            $x = $search;\n            $search = [$x];\n        }\n        if (!is_array($replacement)) {\n            $x = $replacement;\n            $replacement = [$x]; // mPDF 5.7.4\n        }\n\n        if (!$this->onlyCoreFonts && !$this->usingCoreFont) {\n            foreach ($search as $k => $val) {\n                $search[$k] = $this->writer->utf8ToUtf16BigEndian($search[$k], false);\n                $search[$k] = $this->writer->escape($search[$k]);\n                $replacement[$k] = $this->writer->utf8ToUtf16BigEndian($replacement[$k], false);\n                $replacement[$k] = $this->writer->escape($replacement[$k]);\n            }\n        } else {\n            foreach ($replacement as $k => $val) {\n                $replacement[$k] = mb_convert_encoding($replacement[$k], $this->mb_enc, 'utf-8');\n                $replacement[$k] = $this->writer->escape($replacement[$k]);\n            }\n        }\n\n        // Get xref into array\n        $xref = [];\n        preg_match(\"/xref\\n0 (\\d+)\\n(.*?)\\ntrailer/s\", $pdf, $m);\n        $xref_objid = $m[1];\n        preg_match_all('/(\\d{10}) (\\d{5}) (f|n)/', $m[2], $x);\n        for ($i = 0; $i < count($x[0]); $i++) {\n            $xref[] = [intval($x[1][$i]), $x[2][$i], $x[3][$i]];\n        }\n\n        $changes = [];\n        preg_match(\"/<<\\s*\\/Type\\s*\\/Pages\\s*\\/Kids\\s*\\[(.*?)\\]\\s*\\/Count/s\", $pdf, $m);\n        preg_match_all(\"/(\\d+) 0 R /s\", $m[1], $o);\n        $objlist = $o[1];\n\n        foreach ($objlist as $obj) {\n            if ($this->compress) {\n                preg_match(\"/\" . ($obj + 1) . \" 0 obj\\n<<\\s*\\/Filter\\s*\\/FlateDecode\\s*\\/Length (\\d+)>>\\nstream\\n(.*?)\\nendstream\\n/s\", $pdf, $m);\n            } else {\n                preg_match(\"/\" . ($obj + 1) . \" 0 obj\\n<<\\s*\\/Length (\\d+)>>\\nstream\\n(.*?)\\nendstream\\n/s\", $pdf, $m);\n            }\n\n            $s = $m[2];\n            if (!$s) {\n                continue;\n            }\n\n            $oldlen = $m[1];\n\n            if ($this->encrypted) {\n                $s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);\n            }\n\n            if ($this->compress) {\n                $s = gzuncompress($s);\n            }\n\n            foreach ($search as $k => $val) {\n                $s = str_replace($search[$k], $replacement[$k], $s);\n            }\n\n            if ($this->compress) {\n                $s = gzcompress($s);\n            }\n\n            if ($this->encrypted) {\n                $s = $this->protection->rc4($this->protection->objectKey($obj + 1), $s);\n            }\n\n            $newlen = strlen($s);\n\n            $changes[($xref[$obj + 1][0])] = ($newlen - $oldlen) + (strlen($newlen) - strlen($oldlen));\n\n            if ($this->compress) {\n                $newstr = ($obj + 1) . \" 0 obj\\n<</Filter /FlateDecode /Length \" . $newlen . \">>\\nstream\\n\" . $s . \"\\nendstream\\n\";\n            } else {\n                $newstr = ($obj + 1) . \" 0 obj\\n<</Length \" . $newlen . \">>\\nstream\\n\" . $s . \"\\nendstream\\n\";\n            }\n\n            $pdf = str_replace($m[0], $newstr, $pdf);\n        }\n\n        // Update xref in PDF\n        krsort($changes);\n        $newxref = \"xref\\n0 \" . $xref_objid . \"\\n\";\n        foreach ($xref as $v) {\n            foreach ($changes as $ck => $cv) {\n                if ($v[0] > $ck) {\n                    $v[0] += $cv;\n                }\n            }\n            $newxref .= sprintf('%010d', $v[0]) . ' ' . $v[1] . ' ' . $v[2] . \" \\n\";\n        }\n        $newxref .= \"trailer\";\n        $pdf = preg_replace(\"/xref\\n0 \\d+\\n.*?\\ntrailer/s\", $newxref, $pdf);\n\n        // Update startxref in PDF\n        preg_match(\"/startxref\\n(\\d+)\\n%%EOF/s\", $pdf, $m);\n        $startxref = $m[1];\n        $startxref += array_sum($changes);\n        $pdf = preg_replace(\"/startxref\\n(\\d+)\\n%%EOF/s\", \"startxref\\n\" . $startxref . \"\\n%%EOF\", $pdf);\n\n        // OUTPUT\n        switch ($dest) {\n            case Destination::INLINE:\n                if (isset($_SERVER['SERVER_NAME'])) {\n                    // We send to a browser\n                    header('Content-Type: application/pdf');\n                    header('Content-Length: ' . strlen($pdf));\n                    header('Content-disposition: inline; filename=' . $file_out);\n                }\n\n                echo $pdf;\n\n                break;\n\n            case Destination::FILE:\n                if (!$file_out) {\n                    $file_out = 'mpdf.pdf';\n                }\n\n                $f = fopen($file_out, 'wb');\n\n                if (!$f) {\n                    throw new \\Mpdf\\MpdfException('Unable to create output file: ' . $file_out);\n                }\n\n                fwrite($f, $pdf, strlen($pdf));\n\n                fclose($f);\n\n                break;\n\n            case Destination::STRING_RETURN:\n                return $pdf;\n\n            case Destination::DOWNLOAD: // Download file\n            default:\n                if (isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {\n                    header('Content-Type: application/force-download');\n                } else {\n                    header('Content-Type: application/octet-stream');\n                }\n\n                header('Content-Length: ' . strlen($pdf));\n                header('Content-disposition: attachment; filename=' . $file_out);\n\n                echo $pdf;\n\n                break;\n        }\n    }\n\n\n    function Thumbnail($file, $npr = 3, $spacing = 10)\n    {\n        // $npr = number per row\n        $w = (($this->pgwidth + $spacing) / $npr) - $spacing;\n        $oldlinewidth = $this->LineWidth;\n        $this->SetLineWidth(0.02);\n        $this->SetDColor($this->colorConverter->convert(0, $this->PDFAXwarnings));\n        $h = 0;\n        $maxh = 0;\n        $x = $_x = $this->lMargin;\n        $_y = $this->tMargin;\n\n        if ($this->y == 0) {\n            $y = $_y;\n        } else {\n            $y = $this->y;\n        }\n\n        $pagecount = $this->setSourceFile($file);\n\n        for ($n = 1; $n <= $pagecount; $n++) {\n            $tplidx = $this->importPage($n);\n            $size = $this->useTemplate($tplidx, $x, $y, $w);\n            $this->Rect($x, $y, $size['width'], $size['height']);\n            $h = max($h, $size['height']);\n            $maxh = max($h, $maxh);\n\n            if ($n % $npr == 0) {\n                if (($y + $h + $spacing + $maxh) > $this->PageBreakTrigger && $n != $pagecount) {\n                    $this->AddPage();\n                    $x = $_x;\n                    $y = $_y;\n                } else {\n                    $y += $h + $spacing;\n                    $x = $_x;\n                    $h = 0;\n                }\n            } else {\n                $x += $w + $spacing;\n            }\n        }\n        $this->SetLineWidth($oldlinewidth);\n    }\n\n    function SetPageTemplate($tplidx = '')\n    {\n        if (!isset($this->importedPages[$tplidx])) {\n            $this->pageTemplate = '';\n            return false;\n        }\n        $this->pageTemplate = $tplidx;\n    }\n\n    function SetDocTemplate($file = '', $continue = 0)\n    {\n        $this->docTemplate = $file;\n        $this->docTemplateContinue = $continue;\n    }\n\n    /* -- END IMPORTS -- */\n\n    // JAVASCRIPT\n    function _set_object_javascript($string)\n    {\n        $this->writer->object();\n        $this->writer->write('<<');\n        $this->writer->write('/S /JavaScript ');\n        $this->writer->write('/JS ' . $this->writer->string($string));\n        $this->writer->write('>>');\n        $this->writer->write('endobj');\n    }\n\n    function SetJS($script)\n    {\n        $this->js = $script;\n    }\n\n    /**\n     * This function takes the last comma or dot (if any) to make a clean float, ignoring thousand separator, currency or any other letter\n     *\n     * @param string $num\n     * @see http://php.net/manual/de/function.floatval.php#114486\n     * @return float\n     */\n    public function toFloat($num)\n    {\n        $dotPos = strrpos($num, '.');\n        $commaPos = strrpos($num, ',');\n        $sep = (($dotPos > $commaPos) && $dotPos) ? $dotPos : ((($commaPos > $dotPos) && $commaPos) ? $commaPos : false);\n\n        if (!$sep) {\n            return floatval(preg_replace('/[^0-9]/', '', $num));\n        }\n\n        return floatval(\n            preg_replace('/[^0-9]/', '', substr($num, 0, $sep)) . '.' .\n            preg_replace('/[^0-9]/', '', substr($num, $sep+1, strlen($num)))\n        );\n    }\n\n    public function getFontDescriptor()\n    {\n        return $this->fontDescriptor;\n    }\n\n    /**\n     * Temporarily return the method to preserve example 44 yearbook\n     */\n    public function _out($s)\n    {\n        $this->writer->write($s);\n    }\n\n}\n"
  },
  {
    "path": "tests/benchmark/ContextFactoryBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace benchmark;\n\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\Warmup;\nuse phpDocumentor\\Reflection\\Types\\ContextFactory;\n\n/**\n * @BeforeMethods({\"setup\"})\n */\nfinal class ContextFactoryBench\n{\n    private string $source;\n\n    public function setup(): void\n    {\n        $this->source = (string) file_get_contents(__DIR__ . '/Assets/mpdf.php');\n    }\n\n    /**\n     * @Warmup(1)\n     */\n    public function benchCreateContextForNamespace(): void\n    {\n        $factory = new ContextFactory();\n        $factory->createForNamespace(\n            'Mpdf',\n            $this->source\n        );\n    }\n}\n"
  },
  {
    "path": "tests/benchmark/TypeResolverBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace benchmark;\n\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\Revs;\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\Warmup;\nuse phpDocumentor\\Reflection\\TypeResolver;\n\n/**\n * @BeforeMethods({\"setup\"})\n */\nclass TypeResolverBench\n{\n    private TypeResolver $typeResolver;\n\n    public function setup(): void\n    {\n        $this->typeResolver = new TypeResolver();\n    }\n\n    /**\n     * @Warmup(2)\n     * @Revs(10000)\n     */\n    public function benchResolveSingleType() : void\n    {\n        $this->typeResolver->resolve('string');\n    }\n\n    /**\n     * @Warmup(2)\n     * @Revs(10000)\n     */\n    public function benchResolveCompoundType() : void\n    {\n        $this->typeResolver->resolve('string|int|bool');\n    }\n\n    /**\n     * @Warmup(2)\n     * @Revs(10000)\n     */\n    public function benchResolveArrayType() : void\n    {\n        $this->typeResolver->resolve('string[]');\n    }\n\n    /**\n     * @Warmup(2)\n     * @Revs(10000)\n     */\n    public function benchResolveCompoundArrayType() : void\n    {\n        $this->typeResolver->resolve('(string|int)[]');\n    }\n\n    /**\n     * @Warmup(2)\n     * @Revs(10000)\n     */\n    public function benchResolveCompoundArrayWithDefinedTypes() : void\n    {\n        $this->typeResolver->resolve('array<int, string>|array<int, int>');\n    }\n}\n"
  },
  {
    "path": "tests/benchmark/TypeResolverWithContextBench.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace benchmark;\n\nuse PhpBench\\Benchmark\\Metadata\\Annotations\\Warmup;\nuse phpDocumentor\\Reflection\\TypeResolver;\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse phpDocumentor\\Reflection\\Types\\ContextFactory;\n\n/**\n * @BeforeMethods({\"setup\"})\n */\nfinal class TypeResolverWithContextBench\n{\n    /**\n     * @var Context\n     */\n    private $context;\n\n    /**\n     * @var TypeResolver\n     */\n    private $typeResolver;\n\n    public function setup(): void\n    {\n        $factory = new ContextFactory();\n        $this->context = $factory->createForNamespace('mpdf', (string) file_get_contents(__DIR__ . '/Assets/mpdf.php'));\n        $this->typeResolver = new TypeResolver();\n    }\n\n    /**\n     * @Warmup(2)\n     */\n    public function benchResolveCompoundArrayWithDefinedTypes() : void\n    {\n        $this->typeResolver->resolve('array<int, string>|array<int, int>', $this->context);\n    }\n\n    /**\n     * @Warmup(2)\n     */\n    public function benchArrayOfClass() : void\n    {\n        $this->typeResolver->resolve('Conversion[]', $this->context);\n    }\n}\n"
  },
  {
    "path": "tests/unit/CollectionResolverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\nuse phpDocumentor\\Reflection\\PseudoTypes\\Generic;\nuse phpDocumentor\\Reflection\\PseudoTypes\\List_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyList;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse phpDocumentor\\Reflection\\Types\\Float_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Nullable;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\nuse RuntimeException;\n\nclass CollectionResolverTest extends TestCase\n{\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\PseudoTypes\\Generic\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingCollection(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('ArrayObject<string>', new Context(''));\n\n        $this->assertInstanceOf(Generic::class, $resolvedType);\n        $this->assertSame('\\\\ArrayObject<string>', (string) $resolvedType);\n        $this->assertSame('\\\\ArrayObject', (string) $resolvedType->getFqsen());\n        $this->assertEquals([new String_()], $resolvedType->getTypes());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\PseudoTypes\\Generic\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingCollectionWithKeyType(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('ArrayObject<string[],Iterator>', new Context(''));\n\n        $this->assertInstanceOf(Generic::class, $resolvedType);\n        $this->assertSame('\\\\ArrayObject<string[], \\\\Iterator>', (string) $resolvedType);\n        $this->assertSame('\\\\ArrayObject', (string) $resolvedType->getFqsen());\n\n        $types = $resolvedType->getTypes();\n\n        $this->assertArrayHasKey(0, $types);\n        $this->assertEquals(new Array_(new String_()), $types[0]);\n        $this->assertArrayHasKey(1, $types);\n        $this->assertEquals(new Object_(new Fqsen('\\\\Iterator')), $types[1]);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingArrayCollection(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('array<string>', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('string[]', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $keyType = $resolvedType->getKeyType();\n\n        $this->assertInstanceOf(String_::class, $valueType);\n        $this->assertInstanceOf(Compound::class, $keyType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingArrayCollectionWithKey(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('array<string, object|array>', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<string, object|array>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $keyType = $resolvedType->getKeyType();\n\n        $this->assertInstanceOf(String_::class, $keyType);\n        $this->assertInstanceOf(Compound::class, $valueType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingArrayCollectionWithKeyAndWhitespace(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('array<string, object|array>', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<string, object|array>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $keyType = $resolvedType->getKeyType();\n\n        $this->assertInstanceOf(String_::class, $keyType);\n        $this->assertInstanceOf(Compound::class, $valueType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\PseudoTypes\\Generic\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingCollectionOfCollection(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('ArrayObject<string|integer|double,ArrayObject<DateTime>>', new Context(''));\n\n        $this->assertInstanceOf(Generic::class, $resolvedType);\n        $this->assertSame('\\\\ArrayObject<string|int|float, \\\\ArrayObject<\\\\DateTime>>', (string) $resolvedType);\n        $this->assertSame('\\\\ArrayObject', (string) $resolvedType->getFqsen());\n\n        $types = $resolvedType->getTypes();\n\n        $this->assertArrayHasKey(0, $types);\n        $this->assertEquals(new Compound([new String_(), new Integer(), new Float_()]), $types[0]);\n\n        $this->assertArrayHasKey(1, $types);\n        $this->assertInstanceOf(Generic::class, $types[1]);\n        $this->assertSame('\\\\ArrayObject', (string) $types[1]->getFqsen());\n\n        $nestedGenericTypes = $types[1]->getTypes();\n        $this->assertArrayHasKey(0, $nestedGenericTypes);\n        $this->assertEquals(new Object_(new Fqsen('\\\\DateTime')), $nestedGenericTypes[0]);\n    }\n\n    public function testGoodArrayCollectionKey(): void\n    {\n        $fixture = new TypeResolver();\n        $resolvedType = $fixture->resolve('array<array-key, string>', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<array-key, string>', (string) $resolvedType);\n\n        $fixture = new TypeResolver();\n        $resolvedType = $fixture->resolve('array<class-string, string>', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<class-string, string>', (string) $resolvedType);\n    }\n\n    public function testMissingStartCollection(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Unexpected token \"<\", expected type at offset 0');\n        $fixture = new TypeResolver();\n        $fixture->resolve('<string>', new Context(''));\n    }\n\n    public function testMissingEndCollection(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Unexpected token \"\", expected \\'>\\' at offset 25');\n        $fixture = new TypeResolver();\n        $fixture->resolve('ArrayObject<object|string', new Context(''));\n    }\n\n    public function testBadCollectionClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('string is an unsupported generic');\n        $fixture = new TypeResolver();\n        $fixture->resolve('string<integer>', new Context(''));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingCollectionAsArray(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('array<string,float>', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<string, float>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $keyType = $resolvedType->getKeyType();\n\n        $this->assertInstanceOf(Float_::class, $valueType);\n        $this->assertInstanceOf(String_::class, $keyType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingList(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('list<string>', new Context(''));\n\n        $this->assertInstanceOf(List_::class, $resolvedType);\n        $this->assertSame('list<string>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $keyType = $resolvedType->getKeyType();\n\n        $this->assertInstanceOf(String_::class, $valueType);\n        $this->assertInstanceOf(Integer::class, $keyType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingNonEmptyList(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('non-empty-list<string>', new Context(''));\n\n        $this->assertInstanceOf(NonEmptyList::class, $resolvedType);\n        $this->assertSame('non-empty-list<string>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $keyType = $resolvedType->getKeyType();\n\n        $this->assertInstanceOf(String_::class, $valueType);\n        $this->assertInstanceOf(Integer::class, $keyType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Nullable\n     */\n    public function testResolvingNullableArray(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('?array<int>', new Context(''));\n\n        $this->assertInstanceOf(Nullable::class, $resolvedType);\n        $this->assertSame('?int[]', (string) $resolvedType);\n    }\n}\n"
  },
  {
    "path": "tests/unit/FqsenResolverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\nuse InvalidArgumentException;\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class FqsenResolverTest extends TestCase\n{\n    public function testResolveFqsen(): void\n    {\n        $fqsenResolver = new FqsenResolver();\n\n        $context = new Context('', []);\n\n        $result = $fqsenResolver->resolve('\\DocBlock', $context);\n        static::assertSame('\\DocBlock', (string) $result);\n    }\n\n    public function testResolveFqsenWithEmoji(): void\n    {\n        $fqsenResolver = new FqsenResolver();\n\n        $context = new Context('', []);\n\n        $result = $fqsenResolver->resolve('\\My😁DocBlock', $context);\n        static::assertSame('\\My😁DocBlock', (string) $result);\n    }\n\n    public function testResolveWithoutContext(): void\n    {\n        $fqsenResolver = new FqsenResolver();\n\n        $result = $fqsenResolver->resolve('\\DocBlock');\n        static::assertSame('\\DocBlock', (string) $result);\n    }\n\n    public function testResolveFromAlias(): void\n    {\n        $fqsenResolver = new FqsenResolver();\n\n        $context = new Context('somens', ['ns' => 'some\\other\\ns']);\n\n        $result = $fqsenResolver->resolve('ns', $context);\n        static::assertSame('\\some\\other\\ns', (string) $result);\n    }\n\n    public function testResolveFromPartialAlias(): void\n    {\n        $fqsenResolver = new FqsenResolver();\n\n        $context = new Context('somens', ['other' => 'some\\other']);\n\n        $result = $fqsenResolver->resolve('other\\ns', $context);\n        static::assertSame('\\some\\other\\ns', (string) $result);\n    }\n\n    public function testResolveThrowsExceptionWhenGarbageInputIsPassed(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $fqsenResolver = new FqsenResolver();\n\n        $context = new Context('', []);\n\n        $fqsenResolver->resolve('this is complete garbage', $context);\n    }\n}\n"
  },
  {
    "path": "tests/unit/IntegerRangeResolverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange;\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse PHPUnit\\Framework\\TestCase;\nuse RuntimeException;\n\nclass IntegerRangeResolverTest extends TestCase\n{\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingIntRange(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('int<-5, 5>', new Context(''));\n\n        $this->assertInstanceOf(IntegerRange::class, $resolvedType);\n        $this->assertSame('int<-5, 5>', (string) $resolvedType);\n\n        $minValue = $resolvedType->getMinValue();\n        $maxValue = $resolvedType->getMaxValue();\n\n        $this->assertSame('-5', $minValue);\n        $this->assertSame('5', $maxValue);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingIntRangeWithKeywords(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('int<min,max>', new Context(''));\n\n        $this->assertInstanceOf(IntegerRange::class, $resolvedType);\n        $this->assertSame('int<min, max>', (string) $resolvedType);\n\n        $minValue = $resolvedType->getMinValue();\n        $maxValue = $resolvedType->getMaxValue();\n\n        $this->assertSame('min', $minValue);\n        $this->assertSame('max', $maxValue);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingIntRangeErrorMissingMaxValue(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('int<min,max> has not the correct format');\n\n        $fixture = new TypeResolver();\n        $resolvedType = $fixture->resolve('int<min,>', new Context(''));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingIntRangeErrorMisingMinValue(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Unexpected token \",\", expected type at offset 4');\n\n        $fixture = new TypeResolver();\n        $resolvedType = $fixture->resolve('int<,max>', new Context(''));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingIntRangeErrorMisingComma(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('int<min,max> has not the correct format');\n\n        $fixture = new TypeResolver();\n        $resolvedType = $fixture->resolve('int<min|max>', new Context(''));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingIntRangeErrorMissingEnd(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Unexpected token \"\", expected \\'>\\' at offset 11');\n\n        $fixture = new TypeResolver();\n        $resolvedType = $fixture->resolve('int<min,max', new Context(''));\n    }\n}\n"
  },
  {
    "path": "tests/unit/NumericResolverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\nuse phpDocumentor\\Reflection\\PseudoTypes\\Numeric_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NumericString;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nclass NumericResolverTest extends TestCase\n{\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingIntRange(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('numeric', new Context(''));\n\n        $this->assertInstanceOf(Numeric_::class, $resolvedType);\n        $this->assertSame('numeric', (string) $resolvedType);\n\n        $underlyingType = $resolvedType->underlyingType();\n        $this->assertInstanceOf(Compound::class, $underlyingType);\n        $this->assertFalse($underlyingType->contains(new String_()));\n        $this->assertTrue($underlyingType->contains(new NumericString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ArrayKeyTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ArrayKeyTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new ArrayKey();\n\n        $this->assertEquals(new Compound([new String_(), new Integer()]), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('array-key', (string) (new ArrayKey()));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\PseudoTypes\\ArrayKey::__construct\n     */\n    public function testArrayKeyCanBeIterated(): void\n    {\n        $types = [String_::class, Integer::class];\n\n        foreach (new ArrayKey() as $index => $type) {\n            $this->assertInstanceOf($types[$index], $type);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ArrayShapeItemTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ArrayShapeItemTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $key = 'abc';\n        $value = new IntegerValue(100);\n        $item = new ArrayShapeItem($key, $value, true);\n\n        $this->assertSame($key, $item->getKey());\n        $this->assertSame($value, $item->getValue());\n        $this->assertTrue($item->isOptional());\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ArrayShapeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ArrayShapeTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $item1 = new ArrayShapeItem('foo', new True_(), false);\n        $item2 = new ArrayShapeItem('bar', new False_(), true);\n\n        $arrayShape = new ArrayShape($item1, $item2);\n\n        $this->assertSame([$item1, $item2], $arrayShape->getItems());\n        $this->assertEquals(new Array_(new Mixed_(), new ArrayKey()), $arrayShape->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, ArrayShape $arrayShape): void\n    {\n        $this->assertSame($expectedResult, (string) $arrayShape);\n    }\n\n    /**\n     * @return array<string, array{string, ArrayShape}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'with keys' => [\n                'array{foo: true, bar?: false}',\n                new ArrayShape(\n                    new ArrayShapeItem('foo', new True_(), false),\n                    new ArrayShapeItem('bar', new False_(), true)\n                ),\n            ],\n            'with empty keys' => [\n                'array{true, false}',\n                new ArrayShape(\n                    new ArrayShapeItem('', new True_(), false),\n                    new ArrayShapeItem('', new False_(), false)\n                ),\n            ],\n            'without keys' => [\n                'array{true, false}',\n                new ArrayShape(\n                    new ArrayShapeItem(null, new True_(), false),\n                    new ArrayShapeItem(null, new False_(), false)\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/CallableArrayTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class CallableArrayTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new CallableArray();\n\n        $this->assertEquals(new Array_(new Mixed_(), new Integer()), $type->underlyingType());\n        $this->assertEquals(new Mixed_(), $type->getValueType());\n        $this->assertEquals(new Integer(), $type->getKeyType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('callable-array', (string) (new CallableArray()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/CallableStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class CallableStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new CallableString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('callable-string', (string) (new CallableString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ClassStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ClassStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $genericType = new Object_(new Fqsen('\\Foo\\Bar'));\n        $type = new ClassString($genericType);\n\n        $this->assertSame($genericType, $type->getGenericType());\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(ClassString $type, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{ClassString, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'generic class string' => [new ClassString(), 'class-string'],\n            'typed class string' => [\n                new ClassString(new Object_(new Fqsen('\\Foo\\Bar'))),\n                'class-string<\\Foo\\Bar>',\n            ],\n            'more than one class' => [\n                new ClassString(\n                    new Compound([\n                        new Object_(new Fqsen('\\Foo\\Bar')),\n                        new Object_(new Fqsen('\\Foo\\Barrr')),\n                    ])\n                ),\n                'class-string<\\Foo\\Bar|\\Foo\\Barrr>',\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ClosedResourceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Resource_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ClosedResourceTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new ClosedResource();\n\n        $this->assertEquals(new Resource_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('closed-resource', (string) (new ClosedResource()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ConditionalForParameterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Static_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ConditionalForParameterTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $parameterName = 'some';\n        $targetType = new Integer();\n        $if = new Static_();\n        $else = new Array_(new Static_());\n        $type = new ConditionalForParameter(false, $parameterName, $targetType, $if, $else);\n\n        $this->assertFalse($type->isNegated());\n        $this->assertSame($parameterName, $type->getParameterName());\n        $this->assertSame($targetType, $type->getTargetType());\n        $this->assertSame($if, $type->getIf());\n        $this->assertSame($else, $type->getElse());\n        $this->assertEquals(new Mixed_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, ConditionalForParameter $type): void\n    {\n        $this->assertSame($expectedResult, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{string, ConditionalForParameter}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'basic' => [\n                '($test is int ? static : static[])',\n                new ConditionalForParameter(\n                    false,\n                    'test',\n                    new Integer(),\n                    new Static_(),\n                    new Array_(new Static_())\n                ),\n            ],\n            'negated' => [\n                '($test2 is not int ? static : static[])',\n                new ConditionalForParameter(\n                    true,\n                    'test2',\n                    new Integer(),\n                    new Static_(),\n                    new Array_(new Static_())\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ConditionalTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\Static_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ConditionalTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $subjectType = new Object_(new Fqsen('\\\\phpDocumentor\\\\T'));\n        $targetType = new Integer();\n        $if = new Static_();\n        $else = new Array_(new Static_());\n        $type = new Conditional(false, $subjectType, $targetType, $if, $else);\n\n        $this->assertFalse($type->isNegated());\n        $this->assertSame($subjectType, $type->getSubjectType());\n        $this->assertSame($targetType, $type->getTargetType());\n        $this->assertSame($if, $type->getIf());\n        $this->assertSame($else, $type->getElse());\n        $this->assertEquals(new Mixed_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, Conditional $type): void\n    {\n        $this->assertSame($expectedResult, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{string, Conditional}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'basic' => [\n                '(\\\\phpDocumentor\\\\T is int ? static : static[])',\n                new Conditional(\n                    false,\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\T')),\n                    new Integer(),\n                    new Static_(),\n                    new Array_(new Static_())\n                ),\n            ],\n            'negated' => [\n                '(\\\\phpDocumentor\\\\T is not int ? static : static[])',\n                new Conditional(\n                    true,\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\T')),\n                    new Integer(),\n                    new Static_(),\n                    new Array_(new Static_())\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ConstExpressionTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ConstExpressionTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $owner = new Object_(new Fqsen('\\Foo\\Bar'));\n        $expression = '*';\n        $type = new ConstExpression($owner, $expression);\n\n        $this->assertSame($owner, $type->getOwner());\n        $this->assertSame($expression, $type->getExpression());\n        $this->assertEquals(new Mixed_(), $type->underlyingType());\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/EnumStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class EnumStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $genericType = new Object_(new Fqsen('\\Foo\\Bar'));\n        $type = new EnumString($genericType);\n\n        $this->assertSame($genericType, $type->getGenericType());\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(EnumString $type, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{EnumString, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'basic' => [new EnumString(), 'enum-string'],\n            'with generics' => [\n                new EnumString(new Object_(new Fqsen('\\Foo\\Bar'))),\n                'enum-string<\\Foo\\Bar>',\n            ],\n            'more than one enum' => [\n                new EnumString(\n                    new Compound([\n                        new Object_(new Fqsen('\\Foo\\Bar')),\n                        new Object_(new Fqsen('\\Foo\\Barrr')),\n                    ])\n                ),\n                'enum-string<\\Foo\\Bar|\\Foo\\Barrr>',\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/FalseTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Boolean;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class FalseTest extends TestCase\n{\n    public function testExposesUnderlyingType(): void\n    {\n        $false = new False_();\n\n        $this->assertInstanceOf(Boolean::class, $false->underlyingType());\n    }\n\n    public function testFalseStringifyCorrectly(): void\n    {\n        $false = new False_();\n\n        $this->assertSame('false', (string) $false);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/FloatValueTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Float_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class FloatValueTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $value = 12.12;\n        $type = new FloatValue($value);\n\n        $this->assertSame($value, $type->getValue());\n        $this->assertEquals(new Float_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('12.12', (string) (new FloatValue(12.12)));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/GenericTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class GenericTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $fqsen = new Fqsen('\\\\Foo\\\\Bar');\n        $types = [new Object_(new Fqsen('\\\\Foo\\\\SomeClass')), new List_(new Integer())];\n        $type = new Generic($fqsen, $types);\n\n        $this->assertSame($fqsen, $type->getFqsen());\n        $this->assertSame($types, $type->getTypes());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, Generic $type): void\n    {\n        $this->assertSame($expectedResult, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{string, Generic}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'without fqsen' => [\n                'object<string>',\n                new Generic(\n                    null,\n                    [\n                        new String_(),\n                    ]\n                ),\n            ],\n            'collection without key' => [\n                '\\\\ArrayObject<string>',\n                new Generic(new Fqsen('\\\\ArrayObject'), [new String_()]),\n            ],\n            'collection with key' => [\n                '\\\\ArrayObject<string[], \\\\Iterator>',\n                new Generic(\n                    new Fqsen('\\\\ArrayObject'),\n                    [new Array_(new String_()), new Object_(new Fqsen('\\\\Iterator'))]\n                ),\n            ],\n            'more than two generics' => [\n                '\\\\MyClass<\\\\T, \\\\SomeClassSecond, \\\\SomeClassThird>',\n                new Generic(\n                    new Fqsen('\\\\MyClass'),\n                    [\n                        new Object_(new Fqsen('\\\\T')),\n                        new Object_(new Fqsen('\\\\SomeClassSecond')),\n                        new Object_(new Fqsen('\\\\SomeClassThird')),\n                    ]\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/HtmlEscapedStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class HtmlEscapedStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new HtmlEscapedString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('html-escaped-string', (string) (new HtmlEscapedString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/IntMaskOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class IntMaskOfTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $childType = new Compound([new IntegerValue(1), new IntegerValue(5), new IntegerValue(10)]);\n        $type = new IntMaskOf($childType);\n\n        $this->assertSame($childType, $type->getType());\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $type = new IntMaskOf(new Compound([new IntegerValue(1), new IntegerValue(5), new IntegerValue(10)]));\n\n        $this->assertSame('int-mask-of<1|5|10>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/IntMaskTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class IntMaskTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $childTypes = [new IntegerValue(1), new IntegerValue(5), new IntegerValue(10)];\n        $type = new IntMask(...$childTypes);\n\n        $this->assertSame($childTypes, $type->getTypes());\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $type = new IntMask(new IntegerValue(1), new IntegerValue(510), new IntegerValue(6000));\n        $this->assertSame('int-mask<1, 510, 6000>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/IntegerRangeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class IntegerRangeTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $minValue = '-5';\n        $maxValue = '5';\n        $type = new IntegerRange($minValue, $maxValue);\n\n        $this->assertSame($minValue, $type->getMinValue());\n        $this->assertSame($maxValue, $type->getMaxValue());\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(IntegerRange $type, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{IntegerRange, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'simple int range' => [new IntegerRange('-5', '5'), 'int<-5, 5>'],\n            'zero int range v1' => [new IntegerRange('0', '1'), 'int<0, 1>'],\n            'zero int range v2' => [new IntegerRange('-5', '0'), 'int<-5, 0>'],\n            'mixed int range' => [new IntegerRange('min', '5'), 'int<min, 5>'],\n            'keyword int range' => [new IntegerRange('min', 'max'), 'int<min, max>'],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/IntegerValueTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class IntegerValueTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $value = 12;\n        $type = new IntegerValue($value);\n\n        $this->assertSame($value, $type->getValue());\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('12', (string) (new IntegerValue(12)));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/InterfaceStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class InterfaceStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $genericType = new Object_(new Fqsen('\\Foo\\Bar'));\n        $type = new InterfaceString($genericType);\n\n        $this->assertSame($genericType, $type->getGenericType());\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(InterfaceString $type, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{InterfaceString, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'generic interface string' => [new InterfaceString(), 'interface-string'],\n            'typed interface string' => [\n                new InterfaceString(new Object_(new Fqsen('\\Foo\\Bar'))),\n                'interface-string<\\Foo\\Bar>',\n            ],\n            'more than one class' => [\n                new InterfaceString(\n                    new Compound([\n                        new Object_(new Fqsen('\\Foo\\Bar')),\n                        new Object_(new Fqsen('\\Foo\\Barrr')),\n                    ])\n                ),\n                'interface-string<\\Foo\\Bar|\\Foo\\Barrr>',\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/KeyOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class KeyOfTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $childType = new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Type')), 'ARRAY_CONST');\n        $type = new KeyOf($childType);\n\n        $this->assertSame($childType, $type->getType());\n        $this->assertEquals(new ArrayKey(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $type = new KeyOf(new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Type')), 'ARRAY_CONST'));\n\n        $this->assertSame('key-of<\\\\phpDocumentor\\\\Type::ARRAY_CONST>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ListShapeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ListShapeTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new ListShape(new ListShapeItem(null, new IntegerValue(1), false));\n\n        $this->assertSame('list{1}', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ListTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ListTest extends TestCase\n{\n    public function testCreateWithoutParams(): void\n    {\n        $type = new List_();\n\n        $this->assertEquals(new Integer(), $type->getOriginalKeyType());\n        $this->assertNull($type->getOriginalValueType());\n        $this->assertEquals(new Integer(), $type->getKeyType());\n        $this->assertEquals(new Mixed_(), $type->getValueType());\n        $this->assertEquals(new Array_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(List_ $array, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $array);\n    }\n\n    /**\n     * @return array<string, array{List_, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'simple list' => [new List_(), 'list'],\n            'list of mixed' => [new List_(new Mixed_()), 'list<mixed>'],\n            'list of single type' => [new List_(new String_()), 'list<string>'],\n            'list of compound type' => [new List_(new Compound([new Integer(), new String_()])), 'list<int|string>'],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/LiteralStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class LiteralStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new LiteralString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('literal-string', (string) (new LiteralString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/LowercaseStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class LowercaseStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new LowercaseString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('lowercase-string', (string) (new LowercaseString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NegativeIntegerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NegativeIntegerTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NegativeInteger();\n\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('negative-int', (string) (new NegativeInteger()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NeverReturnTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Never_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NeverReturnTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NeverReturn();\n\n        $this->assertEquals(new Never_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('never-return', (string) (new NeverReturn()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NeverReturnsTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Never_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NeverReturnsTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NeverReturns();\n\n        $this->assertEquals(new Never_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('never-returns', (string) (new NeverReturns()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NoReturnTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Never_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NoReturnTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NoReturn();\n\n        $this->assertEquals(new Never_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('no-return', (string) (new NoReturn()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonEmptyArrayTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonEmptyArrayTest extends TestCase\n{\n    public function testCreateWithoutParams(): void\n    {\n        $type = new NonEmptyArray();\n\n        $this->assertNull($type->getOriginalKeyType());\n        $this->assertNull($type->getOriginalValueType());\n        $this->assertEquals(new Compound([new String_(), new Integer()]), $type->getKeyType());\n        $this->assertEquals(new Mixed_(), $type->getValueType());\n        $this->assertEquals(new Array_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(NonEmptyArray $type, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{NonEmptyArray, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'simple non-empty-array' => [new NonEmptyArray(), 'non-empty-array'],\n            'non-empty-array of mixed' => [new NonEmptyArray(new Mixed_()), 'non-empty-array<mixed>'],\n            'non-empty-array of single type' => [new NonEmptyArray(new String_()), 'non-empty-array<string>'],\n            'non-empty-array of compound type' =>\n                [\n                    new NonEmptyArray(\n                        new Compound([new Integer(), new String_()]),\n                        new String_()\n                    ),\n                    'non-empty-array<string, int|string>',\n                ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonEmptyListTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonEmptyListTest extends TestCase\n{\n    public function testCreateWithoutParams(): void\n    {\n        $type = new NonEmptyList();\n\n        $this->assertEquals(new Integer(), $type->getOriginalKeyType());\n        $this->assertNull($type->getOriginalValueType());\n        $this->assertEquals(new Integer(), $type->getKeyType());\n        $this->assertEquals(new Mixed_(), $type->getValueType());\n        $this->assertEquals(new Array_(null, new Integer()), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(NonEmptyList $array, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $array);\n    }\n\n    /**\n     * @return array<string, array{NonEmptyList, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'simple non-empty-list' => [new NonEmptyList(), 'non-empty-list'],\n            'non-empty-list of mixed' => [new NonEmptyList(new Mixed_()), 'non-empty-list<mixed>'],\n            'non-empty-list of single type' => [new NonEmptyList(new String_()), 'non-empty-list<string>'],\n            'non-empty-list of compound type' =>\n                [new NonEmptyList(new Compound([new Integer(), new String_()])), 'non-empty-list<int|string>'],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonEmptyLowercaseStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonEmptyLowercaseStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NonEmptyLowercaseString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('non-empty-lowercase-string', (string) (new NonEmptyLowercaseString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonEmptyStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonEmptyStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NonEmptyString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('non-empty-string', (string) (new NonEmptyString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonFalsyStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonFalsyStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NonFalsyString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('non-falsy-string', (string) (new NonFalsyString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonNegativeIntegerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonNegativeIntegerTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NonNegativeInteger();\n\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('non-negative-int', (string) (new NonNegativeInteger()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonPositiveIntegerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonPositiveIntegerTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NonPositiveInteger();\n\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('non-positive-int', (string) (new NonPositiveInteger()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NonZeroIntegerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NonZeroIntegerTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NonZeroInteger();\n\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('non-zero-int', (string) (new NonZeroInteger()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/NumericStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NumericStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new NumericString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('numeric-string', (string) (new NumericString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ObjectShapeTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ObjectShapeTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $item1 = new ObjectShapeItem('foo', new True_(), false);\n        $item2 = new ObjectShapeItem('bar', new False_(), true);\n\n        $objectShape = new ObjectShape($item1, $item2);\n\n        $this->assertSame([$item1, $item2], $objectShape->getItems());\n        $this->assertEquals(new Object_(), $objectShape->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, ObjectShape $objectShape): void\n    {\n        $this->assertSame($expectedResult, (string) $objectShape);\n    }\n\n    /**\n     * @return array<string, array{string, ObjectShape}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'with keys' => [\n                'object{foo: true, bar?: false}',\n                new ObjectShape(\n                    new ObjectShapeItem('foo', new True_(), false),\n                    new ObjectShapeItem('bar', new False_(), true)\n                ),\n            ],\n            'with empty keys' => [\n                'object{true, false}',\n                new ObjectShape(\n                    new ObjectShapeItem('', new True_(), false),\n                    new ObjectShapeItem('', new False_(), false)\n                ),\n            ],\n            'without keys' => [\n                'object{true, false}',\n                new ObjectShape(\n                    new ObjectShapeItem(null, new True_(), false),\n                    new ObjectShapeItem(null, new False_(), false)\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/OffsetAccessTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class OffsetAccessTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $mainType = new Object_(new Fqsen('\\\\phpDocumentor\\\\MyArray'));\n        $offset = new StringValue('bar');\n        $type = new OffsetAccess($mainType, $offset);\n\n        $this->assertSame($mainType, $type->getType());\n        $this->assertSame($offset, $type->getOffset());\n        $this->assertEquals(new Mixed_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, OffsetAccess $type): void\n    {\n        $this->assertSame($expectedResult, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{string, OffsetAccess}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'basic' => [\n                '\\\\phpDocumentor\\\\MyArray[\"bar\"]',\n                new OffsetAccess(new Object_(new Fqsen('\\\\phpDocumentor\\\\MyArray')), new StringValue('bar')),\n            ],\n            'with const expression' => [\n                '(\\\\phpDocumentor\\\\Foo::SOME_ARRAY)[\"bar\"]',\n                new OffsetAccess(\n                    new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')), 'SOME_ARRAY'),\n                    new StringValue('bar')\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/OpenResourceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Resource_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class OpenResourceTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new OpenResource();\n\n        $this->assertEquals(new Resource_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('open-resource', (string) (new OpenResource()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/PositiveIntegerTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class PositiveIntegerTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new PositiveInteger();\n\n        $this->assertEquals(new Integer(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('positive-int', (string) (new PositiveInteger()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/PrivatePropertiesOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Self_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class PrivatePropertiesOfTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new PrivatePropertiesOf(new Self_());\n\n        $this->assertSame('private-properties-of<self>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/PropertiesOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Self_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class PropertiesOfTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $childType = new Self_();\n        $type = new PropertiesOf($childType);\n\n        $this->assertSame($childType, $type->getType());\n        $this->assertEquals(new Array_(new Mixed_(), new String_()), $type->underlyingType());\n        $this->assertEquals(new String_(), $type->getKeyType());\n        $this->assertEquals(new Mixed_(), $type->getValueType());\n    }\n\n    public function testToString(): void\n    {\n        $type = new PropertiesOf(new Self_());\n\n        $this->assertSame('properties-of<self>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ProtectedPropertiesOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Self_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ProtectedPropertiesOfTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new ProtectedPropertiesOf(new Self_());\n\n        $this->assertSame('protected-properties-of<self>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/PublicPropertiesOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Self_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class PublicPropertiesOfTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new PublicPropertiesOf(new Self_());\n\n        $this->assertSame('public-properties-of<self>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ScalarTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Boolean;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Float_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ScalarTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new Scalar();\n\n        $expectedUnderlyingType = new Compound([new String_(), new Integer(), new Float_(), new Boolean()]);\n        $this->assertEquals($expectedUnderlyingType, $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $type = new Scalar();\n\n        $this->assertSame('scalar', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/StringValueTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class StringValueTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $value = 'abcdef';\n        $type = new StringValue($value);\n\n        $this->assertSame($value, $type->getValue());\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('\"abcd\"', (string) (new StringValue('abcd')));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/TraitStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TraitStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $genericType = new Object_(new Fqsen('\\Foo\\Bar'));\n        $type = new TraitString($genericType);\n\n        $this->assertSame($genericType, $type->getGenericType());\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(TraitString $type, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{TraitString, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'basic' => [new TraitString(), 'trait-string'],\n            'with generic' => [\n                new TraitString(new Object_(new Fqsen('\\Foo\\Bar'))),\n                'trait-string<\\Foo\\Bar>',\n            ],\n            'with compound generic' => [\n                new TraitString(\n                    new Compound([\n                        new Object_(new Fqsen('\\Foo\\Bar')),\n                        new Object_(new Fqsen('\\Foo\\Barrr')),\n                    ])\n                ),\n                'trait-string<\\Foo\\Bar|\\Foo\\Barrr>',\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/TrueTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\Boolean;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TrueTest extends TestCase\n{\n    public function testExposesUnderlyingType(): void\n    {\n        $true = new True_();\n\n        $this->assertInstanceOf(Boolean::class, $true->underlyingType());\n    }\n\n    public function testTrueStringifyCorrectly(): void\n    {\n        $true = new True_();\n\n        $this->assertSame('true', (string) $true);\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/TruthyStringTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class TruthyStringTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new TruthyString();\n\n        $this->assertEquals(new String_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('truthy-string', (string) (new TruthyString()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/PseudoTypes/ValueOfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\PseudoTypes;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ValueOfTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $childType = new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Type')), 'ARRAY_CONST');\n        $type = new ValueOf($childType);\n\n        $this->assertSame($childType, $type->getType());\n        $this->assertEquals(new Mixed_(), $type->underlyingType());\n    }\n\n    public function testToString(): void\n    {\n        $type = new ValueOf(new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Type')), 'ARRAY_CONST'));\n\n        $this->assertSame('value-of<\\\\phpDocumentor\\\\Type::ARRAY_CONST>', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/TypeResolverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection;\n\nuse Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations;\nuse InvalidArgumentException;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayKey;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayShapeItem;\nuse phpDocumentor\\Reflection\\PseudoTypes\\CallableArray;\nuse phpDocumentor\\Reflection\\PseudoTypes\\CallableString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ClassString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ClosedResource;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Conditional;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ConditionalForParameter;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ConstExpression;\nuse phpDocumentor\\Reflection\\PseudoTypes\\EnumString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\False_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\FloatValue;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Generic;\nuse phpDocumentor\\Reflection\\PseudoTypes\\HtmlEscapedString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntegerRange;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntegerValue;\nuse phpDocumentor\\Reflection\\PseudoTypes\\InterfaceString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntMask;\nuse phpDocumentor\\Reflection\\PseudoTypes\\IntMaskOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\KeyOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\List_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ListShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ListShapeItem;\nuse phpDocumentor\\Reflection\\PseudoTypes\\LiteralString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\LowercaseString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NegativeInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NeverReturn;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NeverReturns;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyArray;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyList;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyLowercaseString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonEmptyString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonFalsyString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonNegativeInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonPositiveInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NonZeroInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NoReturn;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Numeric_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\NumericString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ObjectShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ObjectShapeItem;\nuse phpDocumentor\\Reflection\\PseudoTypes\\OffsetAccess;\nuse phpDocumentor\\Reflection\\PseudoTypes\\OpenResource;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PositiveInteger;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PrivatePropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ProtectedPropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\PublicPropertiesOf;\nuse phpDocumentor\\Reflection\\PseudoTypes\\Scalar;\nuse phpDocumentor\\Reflection\\PseudoTypes\\StringValue;\nuse phpDocumentor\\Reflection\\PseudoTypes\\TraitString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\True_;\nuse phpDocumentor\\Reflection\\PseudoTypes\\TruthyString;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ValueOf;\nuse phpDocumentor\\Reflection\\Types\\Array_;\nuse phpDocumentor\\Reflection\\Types\\Boolean;\nuse phpDocumentor\\Reflection\\Types\\Callable_;\nuse phpDocumentor\\Reflection\\Types\\CallableParameter;\nuse phpDocumentor\\Reflection\\Types\\Compound;\nuse phpDocumentor\\Reflection\\Types\\Context;\nuse phpDocumentor\\Reflection\\Types\\Expression;\nuse phpDocumentor\\Reflection\\Types\\Float_;\nuse phpDocumentor\\Reflection\\Types\\Integer;\nuse phpDocumentor\\Reflection\\Types\\Intersection;\nuse phpDocumentor\\Reflection\\Types\\Iterable_;\nuse phpDocumentor\\Reflection\\Types\\Mixed_;\nuse phpDocumentor\\Reflection\\Types\\Never_;\nuse phpDocumentor\\Reflection\\Types\\Null_;\nuse phpDocumentor\\Reflection\\Types\\Nullable;\nuse phpDocumentor\\Reflection\\Types\\Object_;\nuse phpDocumentor\\Reflection\\Types\\Parent_;\nuse phpDocumentor\\Reflection\\Types\\Resource_;\nuse phpDocumentor\\Reflection\\Types\\Self_;\nuse phpDocumentor\\Reflection\\Types\\Static_;\nuse phpDocumentor\\Reflection\\Types\\String_;\nuse phpDocumentor\\Reflection\\Types\\This;\nuse phpDocumentor\\Reflection\\Types\\Void_;\nuse PHPUnit\\Framework\\TestCase;\nuse RuntimeException;\nuse stdClass;\n\nuse function get_class;\n\nclass TypeResolverTest extends TestCase\n{\n    use VerifyDeprecations;\n\n    /**\n     * @uses         \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses         \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses         \\phpDocumentor\\Reflection\\Types\\Object_\n     *\n     * @param class-string $expectedClass\n     *\n     * @dataProvider provideKeywords\n     */\n    public function testResolvingKeywords(string $keyword, string $expectedClass): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve($keyword, new Context(''));\n        $this->assertInstanceOf($expectedClass, $resolvedType);\n\n        $resolvedType = $fixture->resolve($keyword);\n        $this->assertInstanceOf($expectedClass, $resolvedType);\n    }\n\n    /**\n     * @uses         \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses         \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses         \\phpDocumentor\\Reflection\\Fqsen\n     * @uses         \\phpDocumentor\\Reflection\\FqsenResolver\n     *\n     * @dataProvider provideFqcn\n     */\n    public function testResolvingFQSENs(string $fqsen): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve($fqsen, new Context(''));\n\n        $this->assertInstanceOf(Object_::class, $resolvedType);\n        $this->assertInstanceOf(Fqsen::class, $resolvedType->getFqsen());\n        $this->assertSame($fqsen, (string) $resolvedType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingRelativeQSENsBasedOnNamespace(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('DocBlock', new Context('phpDocumentor\\Reflection'));\n\n        $this->assertInstanceOf(Object_::class, $resolvedType);\n        $this->assertInstanceOf(Fqsen::class, $resolvedType->getFqsen());\n        $this->assertSame('\\phpDocumentor\\Reflection\\DocBlock', (string) $resolvedType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingRelativeQSENsBasedOnNamespaceAlias(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve(\n            'm\\Array_',\n            new Context('phpDocumentor\\Reflection', ['m' => '\\phpDocumentor\\Reflection\\Types'])\n        );\n\n        $this->assertInstanceOf(Object_::class, $resolvedType);\n        $this->assertInstanceOf(Fqsen::class, $resolvedType->getFqsen());\n        $this->assertSame('\\phpDocumentor\\Reflection\\Types\\Array_', (string) $resolvedType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingTypedArrays(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('string[]', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('string[]', (string) $resolvedType);\n        $this->assertInstanceOf(Compound::class, $resolvedType->getKeyType());\n        $this->assertInstanceOf(String_::class, $resolvedType->getValueType());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Nullable\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingNullableTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('?string', new Context(''));\n\n        $this->assertInstanceOf(Nullable::class, $resolvedType);\n        $this->assertInstanceOf(String_::class, $resolvedType->getActualType());\n        $this->assertSame('?string', (string) $resolvedType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingNestedTypedArrays(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('string[][]', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $childValueType = $resolvedType->getValueType();\n\n        $this->assertSame('string[][]', (string) $resolvedType);\n        $this->assertInstanceOf(Compound::class, $resolvedType->getKeyType());\n        $this->assertInstanceOf(Array_::class, $childValueType);\n\n        $this->assertSame('string[]', (string) $childValueType);\n        $this->assertInstanceOf(Compound::class, $childValueType->getKeyType());\n        $this->assertInstanceOf(String_::class, $childValueType->getValueType());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingCompoundTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('string|Reflection\\DocBlock', new Context('phpDocumentor'));\n\n        $this->assertInstanceOf(Compound::class, $resolvedType);\n        $this->assertSame('string|\\phpDocumentor\\Reflection\\DocBlock', (string) $resolvedType);\n\n        $firstType = $resolvedType->get(0);\n\n        $secondType = $resolvedType->get(1);\n\n        $this->assertInstanceOf(String_::class, $firstType);\n        $this->assertInstanceOf(Object_::class, $secondType);\n        $this->assertInstanceOf(Fqsen::class, $secondType->getFqsen());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingAmpersandCompoundTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve(\n            'Reflection\\DocBlock&\\PHPUnit\\Framework\\MockObject\\MockObject ',\n            new Context('phpDocumentor')\n        );\n\n        $this->assertInstanceOf(Intersection::class, $resolvedType);\n        $this->assertSame(\n            '\\phpDocumentor\\Reflection\\DocBlock&\\PHPUnit\\Framework\\MockObject\\MockObject',\n            (string) $resolvedType\n        );\n\n        $firstType = $resolvedType->get(0);\n\n        $secondType = $resolvedType->get(1);\n\n        $this->assertInstanceOf(Object_::class, $firstType);\n        $this->assertInstanceOf(Fqsen::class, $firstType->getFqsen());\n        $this->assertInstanceOf(Object_::class, $secondType);\n        $this->assertInstanceOf(Fqsen::class, $secondType->getFqsen());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingMixedCompoundTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve(\n            '(Reflection\\DocBlock&\\PHPUnit\\Framework\\MockObject\\MockObject)|null',\n            new Context('phpDocumentor')\n        );\n\n        $this->assertInstanceOf(Compound::class, $resolvedType);\n        $this->assertSame(\n            '(\\phpDocumentor\\Reflection\\DocBlock&\\PHPUnit\\Framework\\MockObject\\MockObject)|null',\n            (string) $resolvedType\n        );\n\n        $firstType = $resolvedType->get(0);\n\n        $secondType = $resolvedType->get(1);\n\n        $this->assertInstanceOf(Expression::class, $firstType);\n        $this->assertSame(\n            '(\\phpDocumentor\\Reflection\\DocBlock&\\PHPUnit\\Framework\\MockObject\\MockObject)',\n            (string) $firstType\n        );\n        $this->assertInstanceOf(Null_::class, $secondType);\n\n        $resolvedType = $firstType->getValueType();\n\n        $this->assertInstanceOf(Intersection::class, $resolvedType);\n        $firstSubType = $resolvedType->get(0);\n        $secondSubType =  $resolvedType->get(1);\n\n        $this->assertInstanceOf(Object_::class, $firstSubType);\n        $this->assertInstanceOf(Fqsen::class, $firstSubType->getFqsen());\n        $this->assertInstanceOf(Object_::class, $secondSubType);\n        $this->assertInstanceOf(Fqsen::class, $secondSubType->getFqsen());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingCompoundTypedArrayTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('\\stdClass[]|Reflection\\DocBlock[]', new Context('phpDocumentor'));\n\n        $this->assertInstanceOf(Compound::class, $resolvedType);\n        $this->assertSame('\\stdClass[]|\\phpDocumentor\\Reflection\\DocBlock[]', (string) $resolvedType);\n\n        $firstType = $resolvedType->get(0);\n\n        $secondType = $resolvedType->get(1);\n\n        $this->assertInstanceOf(Array_::class, $firstType);\n        $this->assertInstanceOf(Array_::class, $secondType);\n        $this->assertInstanceOf(Object_::class, $firstType->getValueType());\n        $this->assertInstanceOf(Object_::class, $secondType->getValueType());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingArrayExpressionObjectsTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('(\\stdClass|Reflection\\DocBlock)[]', new Context('phpDocumentor'));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<\\stdClass|\\phpDocumentor\\Reflection\\DocBlock>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $this->assertInstanceOf(Compound::class, $valueType);\n\n        $firstType = $valueType->get(0);\n\n        $secondType = $valueType->get(1);\n\n        $this->assertInstanceOf(Object_::class, $firstType);\n        $this->assertInstanceOf(Object_::class, $secondType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingArrayExpressionSimpleTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('(string|\\stdClass|boolean)[]', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<string|\\stdClass|bool>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $this->assertInstanceOf(Compound::class, $valueType);\n\n        $firstType = $valueType->get(0);\n\n        $secondType = $valueType->get(1);\n\n        $thirdType = $valueType->get(2);\n\n        $this->assertInstanceOf(String_::class, $firstType);\n        $this->assertInstanceOf(Object_::class, $secondType);\n        $this->assertInstanceOf(Boolean::class, $thirdType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingArrayOfArrayExpressionTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('(string|\\stdClass)[][]', new Context(''));\n\n        $this->assertInstanceOf(Array_::class, $resolvedType);\n        $this->assertSame('array<array<string|\\stdClass>>', (string) $resolvedType);\n\n        $parentArrayType = $resolvedType->getValueType();\n        $this->assertInstanceOf(Array_::class, $parentArrayType);\n\n        $valueType = $parentArrayType->getValueType();\n        $this->assertInstanceOf(Compound::class, $valueType);\n\n        $firstType = $valueType->get(0);\n\n        $secondType = $valueType->get(1);\n\n        $this->assertInstanceOf(String_::class, $firstType);\n        $this->assertInstanceOf(Object_::class, $secondType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testReturnEmptyCompoundOnAnUnclosedArrayExpressionType(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $fixture = new TypeResolver();\n        $fixture->resolve('(string|\\stdClass', new Context(''));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingArrayExpressionOrCompoundTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('\\stdClass|(string|\\stdClass)[]|bool', new Context(''));\n\n        $this->assertInstanceOf(Compound::class, $resolvedType);\n        $this->assertSame('\\stdClass|array<string|\\stdClass>|bool', (string) $resolvedType);\n\n        $firstType = $resolvedType->get(0);\n        $this->assertInstanceOf(Object_::class, $firstType);\n\n        $secondType = $resolvedType->get(1);\n        $this->assertInstanceOf(Array_::class, $secondType);\n\n        $thirdType = $resolvedType->get(2);\n        $this->assertInstanceOf(Boolean::class, $thirdType);\n\n        $valueType = $secondType->getValueType();\n        $this->assertInstanceOf(Compound::class, $valueType);\n\n        $firstArrayType = $valueType->get(0);\n\n        $secondArrayType = $valueType->get(1);\n\n        $this->assertInstanceOf(String_::class, $firstArrayType);\n        $this->assertInstanceOf(Object_::class, $secondArrayType);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Iterable_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Object_\n     * @uses \\phpDocumentor\\Reflection\\Fqsen\n     * @uses \\phpDocumentor\\Reflection\\FqsenResolver\n     */\n    public function testResolvingIterableExpressionSimpleTypes(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('iterable<string|\\stdClass|boolean>', new Context(''));\n\n        $this->assertInstanceOf(Iterable_::class, $resolvedType);\n        $this->assertSame('iterable<string|\\stdClass|bool>', (string) $resolvedType);\n\n        $valueType = $resolvedType->getValueType();\n\n        $this->assertInstanceOf(Compound::class, $valueType);\n\n        $firstType = $valueType->get(0);\n\n        $secondType = $valueType->get(1);\n\n        $thirdType = $valueType->get(2);\n\n        $this->assertInstanceOf(String_::class, $firstType);\n        $this->assertInstanceOf(Object_::class, $secondType);\n        $this->assertInstanceOf(Boolean::class, $thirdType);\n    }\n\n    /**\n     * This test asserts that the parameter order is correct.\n     *\n     * When you pass two arrays separated by the compound operator (i.e. 'integer[]|string[]') then we always split the\n     * expression in its compound parts and then we parse the types with the array operators. If we were to switch the\n     * order around then 'integer[]|string[]' would read as an array of string or integer array; which is something\n     * other than what we intend.\n     *\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound\n     * @uses \\phpDocumentor\\Reflection\\Types\\Array_\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testResolvingCompoundTypesWithTwoArrays(): void\n    {\n        $fixture = new TypeResolver();\n\n        $resolvedType = $fixture->resolve('integer[]|string[]', new Context(''));\n\n        $this->assertInstanceOf(Compound::class, $resolvedType);\n        $this->assertSame('int[]|string[]', (string) $resolvedType);\n\n        $firstType = $resolvedType->get(0);\n\n        $secondType = $resolvedType->get(1);\n\n        $this->assertInstanceOf(Array_::class, $firstType);\n        $this->assertInstanceOf(Integer::class, $firstType->getValueType());\n        $this->assertInstanceOf(Array_::class, $secondType);\n        $this->assertInstanceOf(String_::class, $secondType->getValueType());\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\TypeResolver::resolve\n     * @uses \\phpDocumentor\\Reflection\\TypeResolver::<private>\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     */\n    public function testAddingAKeyword(): void\n    {\n        // Assign\n        $typeMock = self::createStub(Type::class);\n\n        // Act\n        $fixture = new TypeResolver();\n        $fixture->addKeyword('mock', get_class($typeMock));\n\n        // Assert\n        $result = $fixture->resolve('mock', new Context(''));\n        $this->assertInstanceOf(get_class($typeMock), $result);\n        $this->assertNotSame($typeMock, $result);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     */\n    public function testAddingAKeywordFailsIfTypeClassDoesNotExist(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $fixture = new TypeResolver();\n        $fixture->addKeyword('mock', 'IDoNotExist');\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     */\n    public function testAddingAKeywordFailsIfTypeClassDoesNotImplementTypeInterface(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $fixture = new TypeResolver();\n        $fixture->addKeyword('mock', stdClass::class);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     */\n    public function testExceptionIsThrownIfTypeIsEmpty(): void\n    {\n        $this->expectException(InvalidArgumentException::class);\n        $fixture = new TypeResolver();\n        $fixture->resolve(' ', new Context(''));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     */\n    public function testInvalidArrayOperator(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $fixture = new TypeResolver();\n        $fixture->resolve('[]', new Context(''));\n    }\n\n    /**\n     * Returns a list of keywords and expected classes that are created from them.\n     *\n     * @return string[][]\n     */\n    public function provideKeywords(): array\n    {\n        return [\n            ['string', String_::class],\n            ['class-string', ClassString::class],\n            ['html-escaped-string', HtmlEscapedString::class],\n            ['lowercase-string', LowercaseString::class],\n            ['non-empty-lowercase-string', NonEmptyLowercaseString::class],\n            ['non-empty-string', NonEmptyString::class],\n            ['numeric-string', NumericString::class],\n            ['numeric', Numeric_::class],\n            ['trait-string', TraitString::class],\n            ['int', Integer::class],\n            ['integer', Integer::class],\n            ['positive-int', PositiveInteger::class],\n            ['negative-int', NegativeInteger::class],\n            ['float', Float_::class],\n            ['double', Float_::class],\n            ['bool', Boolean::class],\n            ['boolean', Boolean::class],\n            ['true', Boolean::class],\n            ['true', True_::class],\n            ['false', Boolean::class],\n            ['false', False_::class],\n            ['resource', Resource_::class],\n            ['open-resource', OpenResource::class],\n            ['closed-resource', ClosedResource::class],\n            ['null', Null_::class],\n            ['callable', Callable_::class],\n            ['callable-string', CallableString::class],\n            ['callback', Callable_::class],\n            ['array', Array_::class],\n            ['callable-array', CallableArray::class],\n            ['array-key', ArrayKey::class],\n            ['scalar', Scalar::class],\n            ['object', Object_::class],\n            ['mixed', Mixed_::class],\n            ['void', Void_::class],\n            ['$this', This::class],\n            ['static', Static_::class],\n            ['self', Self_::class],\n            ['parent', Parent_::class],\n            ['iterable', Iterable_::class],\n            ['never', Never_::class],\n            ['never-return', NeverReturn::class],\n            ['never-returns', NeverReturns::class],\n            ['no-return', NoReturn::class],\n            ['literal-string', LiteralString::class],\n            ['list', List_::class],\n            ['non-empty-list', NonEmptyList::class],\n            ['non-empty-array', NonEmptyArray::class],\n            ['non-falsy-string', NonFalsyString::class],\n            ['truthy-string', TruthyString::class],\n            ['non-positive-int', NonPositiveInteger::class],\n            ['non-negative-int', NonNegativeInteger::class],\n            ['non-zero-int', NonZeroInteger::class],\n        ];\n    }\n\n    /**\n     * Provides a list of FQSENs to test the resolution patterns with.\n     *\n     * @return string[][]\n     */\n    public function provideFqcn(): array\n    {\n        return [\n            'namespace' => ['\\phpDocumentor\\Reflection'],\n            'class' => ['\\phpDocumentor\\Reflection\\DocBlock'],\n            'class with emoji' => ['\\My😁Class'],\n        ];\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Context\n     */\n    public function testArrayKeyValueSpecification(): void\n    {\n        $fixture = new TypeResolver();\n        $type = $fixture->resolve('array<string,array<int,string>>', new Context(''));\n\n        $this->assertEquals(new Array_(new Array_(new String_(), new Integer()), new String_()), $type);\n    }\n\n    /**\n     * @dataProvider typeProvider\n     * @dataProvider genericsProvider\n     * @dataProvider callableProvider\n     * @dataProvider constExpressions\n     * @dataProvider shapeStructures\n     * @testdox create type from $type\n     */\n    public function testTypeBuilding(string $type, Type $expected, bool $deprecation = false): void\n    {\n        $this->expectNoDeprecationWithIdentifier('https://github.com/phpDocumentor/TypeResolver/issues/184');\n\n        $fixture = new TypeResolver();\n        $actual = $fixture->resolve($type, new Context('phpDocumentor'));\n\n        self::assertEquals($expected, $actual);\n    }\n\n    /**\n     * @dataProvider illegalLegacyFormatProvider\n     * @testdox create type from $type\n     */\n    public function testTypeBuildingThrowsError(string $type, Type $expected): void\n    {\n        $this->expectDeprecationWithIdentifier('https://github.com/phpDocumentor/TypeResolver/issues/184');\n\n        $fixture = new TypeResolver();\n        $actual = $fixture->resolve($type, new Context('phpDocumentor'));\n\n        self::assertEquals($expected, $actual);\n    }\n\n    /**\n     * @return array<array{0: string, 1: Type}>\n     */\n    public function typeProvider(): array\n    {\n        return [\n            [\n                'string',\n                new String_(),\n            ],\n            [\n                '( string )',\n                new String_(),\n            ],\n            [\n                '\\\\Foo\\Bar\\\\Baz',\n                new Object_(new Fqsen('\\\\Foo\\Bar\\\\Baz')),\n            ],\n            [\n                'string|int',\n                new Compound(\n                    [\n                        new String_(),\n                        new Integer(),\n                    ]\n                ),\n            ],\n            [\n                'string&int',\n                new Intersection(\n                    [\n                        new String_(),\n                        new Integer(),\n                    ]\n                ),\n            ],\n            [\n                'string & (int | float)',\n                new Intersection(\n                    [\n                        new String_(),\n                        new Expression(\n                            new Compound(\n                                [\n                                    new Integer(),\n                                    new Float_(),\n                                ]\n                            )\n                        ),\n                    ]\n                ),\n            ],\n            [\n                '(A&B)|C|(D&E)',\n                new Compound([\n                    new Expression(\n                        new Intersection([\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\A')),\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\B')),\n                        ]),\n                    ),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\C')),\n                    new Expression(\n                        new Intersection([\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\D')),\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\E')),\n                        ]),\n                    ),\n                ]),\n            ],\n            [\n                'array',\n                new Array_(),\n            ],\n            [\n                'string[]',\n                new Array_(\n                    new String_()\n                ),\n            ],\n            [\n                'mixed[]',\n                new Array_(\n                    new Mixed_()\n                ),\n            ],\n            [\n                '$this',\n                new This(),\n            ],\n            [\n                '?int',\n                new Nullable(\n                    new Integer()\n                ),\n            ],\n            [\n                'static',\n                new Static_(),\n            ],\n            [\n                'self',\n                new Self_(),\n            ],\n            [\n                '($size is positive-int ? non-empty-array : array)',\n                new ConditionalForParameter(\n                    false,\n                    'size',\n                    new PositiveInteger(),\n                    new NonEmptyArray(),\n                    new Array_()\n                ),\n            ],\n            [\n                '($size is not positive-int ? non-empty-array : int)',\n                new ConditionalForParameter(\n                    true,\n                    'size',\n                    new PositiveInteger(),\n                    new NonEmptyArray(),\n                    new Integer()\n                ),\n            ],\n            [\n                '(T is int ? static : array<static>)',\n                new Conditional(\n                    false,\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\T')),\n                    new Integer(),\n                    new Static_(),\n                    new Array_(new Static_())\n                ),\n            ],\n            [\n                '(T is not int ? self : array<static>)',\n                new Conditional(\n                    true,\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\T')),\n                    new Integer(),\n                    new Self_(),\n                    new Array_(new Static_())\n                ),\n            ],\n            [\n                \"MyArray['bar']\",\n                new OffsetAccess(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\MyArray')),\n                    new StringValue('bar')\n                ),\n            ],\n            [\n                '100.5',\n                new FloatValue(100.5),\n            ],\n        ];\n    }\n\n    /**\n     * @return array<array{0: string, 1: Type}>\n     */\n    public function genericsProvider(): array\n    {\n        return [\n            [\n                'array<int, Foo\\\\Bar>',\n                new Array_(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo\\\\Bar')),\n                    new Integer()\n                ),\n            ],\n            [\n                'array<key-of<Foo\\\\Bar::SOME_CONSTANT>, string>',\n                new Array_(\n                    new String_(),\n                    new KeyOf(new ConstExpression(\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo\\\\Bar')),\n                        'SOME_CONSTANT'\n                    ))\n                ),\n            ],\n            [\n                'array<value-of<Foo\\\\Bar::SOME_CONSTANT>, string>',\n                new Array_(\n                    new String_(),\n                    new ValueOf(new ConstExpression(\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo\\\\Bar')),\n                        'SOME_CONSTANT'\n                    ))\n                ),\n            ],\n            [\n                'array<Foo\\\\Bar::*, string>',\n                new Array_(\n                    new String_(),\n                    new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo\\\\Bar')), '*')\n                ),\n            ],\n            [\n                'array<self::SOME_CONSTANT_FIRST|self::SOME_CONSTANT_SECOND, string>',\n                new Array_(\n                    new String_(),\n                    new Compound([\n                        new ConstExpression(new Self_(), 'SOME_CONSTANT_FIRST'),\n                        new ConstExpression(new Self_(), 'SOME_CONSTANT_SECOND'),\n                    ])\n                ),\n            ],\n            [\n                'array<string|int, Foo\\\\Bar>',\n                new Array_(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo\\\\Bar')),\n                    new Compound(\n                        [\n                            new String_(),\n                            new Integer(),\n                        ]\n                    )\n                ),\n            ],\n            [\n                'non-empty-array<string|int>',\n                new NonEmptyArray(\n                    new Compound(\n                        [\n                            new String_(),\n                            new Integer(),\n                        ]\n                    )\n                ),\n            ],\n            [\n                'non-empty-array<string|int, Foo\\\\Bar>',\n                new NonEmptyArray(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo\\\\Bar')),\n                    new Compound(\n                        [\n                            new String_(),\n                            new Integer(),\n                        ]\n                    )\n                ),\n            ],\n            [\n                'Collection<array-key, int>[]',\n                new Array_(\n                    new Generic(\n                        new Fqsen('\\\\phpDocumentor\\\\Collection'),\n                        [\n                            new ArrayKey(),\n                            new Integer(),\n                        ]\n                    )\n                ),\n            ],\n            [\n                'class-string',\n                new ClassString(),\n            ],\n            [\n                'class-string<Foo>',\n                new ClassString(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                'class-string<Foo|Bar>',\n                new ClassString(\n                    new Compound([\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')),\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Bar')),\n                    ])\n                ),\n            ],\n            [\n                'interface-string',\n                new InterfaceString(),\n            ],\n            [\n                'interface-string<Foo>',\n                new InterfaceString(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                'interface-string<Foo|Bar>',\n                new InterfaceString(\n                    new Compound([\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')),\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Bar')),\n                    ])\n                ),\n            ],\n            [\n                'trait-string',\n                new TraitString(),\n            ],\n            [\n                'trait-string<Foo>',\n                new TraitString(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                'trait-string<Foo|Bar>',\n                new TraitString(\n                    new Compound([\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')),\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\Bar')),\n                    ])\n                ),\n            ],\n            [\n                'enum-string',\n                new EnumString(),\n            ],\n            [\n                'enum-string<MyEnum>',\n                new EnumString(new Object_(new Fqsen('\\\\phpDocumentor\\\\MyEnum'))),\n            ],\n            [\n                'enum-string<MyEnumFirst|MyEnumSecond>',\n                new EnumString(\n                    new Compound([\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\MyEnumFirst')),\n                        new Object_(new Fqsen('\\\\phpDocumentor\\\\MyEnumSecond')),\n                    ])\n                ),\n            ],\n            [\n                'List<Foo>',\n                new List_(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                'non-empty-list<Foo>',\n                new NonEmptyList(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                'int<1, 100>',\n                new IntegerRange('1', '100'),\n            ],\n            [\n                'key-of<Type::ARRAY_CONST>',\n                new KeyOf(new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Type')), 'ARRAY_CONST')),\n            ],\n            [\n                'value-of<Type::ARRAY_CONST>',\n                new ValueOf(new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Type')), 'ARRAY_CONST')),\n            ],\n            [\n                'int-mask<1, 2, 4>',\n                new IntMask(new IntegerValue(1), new IntegerValue(2), new IntegerValue(4)),\n            ],\n            [\n                'int-mask-of<1|2|4>',\n                new IntMaskOf(new Compound([new IntegerValue(1), new IntegerValue(2), new IntegerValue(4)])),\n            ],\n            [\n                'int-mask-of<Foo::INT_*>',\n                new IntMaskOf(new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')), 'INT_*')),\n            ],\n            [\n                'iterable<int, string>',\n                new Iterable_(new String_(), new Integer()),\n            ],\n            [\n                'static<FirstClass, SecondClass, ThirdClass>',\n                new Static_(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\FirstClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\SecondClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\ThirdClass')),\n                ),\n            ],\n            [\n                'self<FirstClass, SecondClass, ThirdClass>',\n                new Self_(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\FirstClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\SecondClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\ThirdClass')),\n                ),\n            ],\n            [\n                'array<mixed>',\n                new Array_(new Mixed_()),\n            ],\n            [\n                'iterable<mixed>',\n                new Iterable_(new Mixed_()),\n            ],\n            [\n                'non-empty-array<mixed>',\n                new NonEmptyArray(new Mixed_()),\n            ],\n            [\n                'non-empty-list<mixed>',\n                new NonEmptyList(new Mixed_()),\n            ],\n            [\n                'properties-of<self>',\n                new PropertiesOf(new Self_()),\n            ],\n            [\n                'public-properties-of<self>',\n                new PublicPropertiesOf(new Self_()),\n            ],\n            [\n                'protected-properties-of<self>',\n                new ProtectedPropertiesOf(new Self_()),\n            ],\n            [\n                'private-properties-of<self>',\n                new PrivatePropertiesOf(new Self_()),\n            ],\n        ];\n    }\n\n    /**\n     * @return array<array{0: string, 1: Type}>\n     */\n    public function callableProvider(): array\n    {\n        return [\n            [\n                'callable',\n                new Callable_(),\n            ],\n            [\n                'pure-callable(int): int',\n                new Callable_(\n                    'pure-callable',\n                    [new CallableParameter(new Integer())],\n                    new Integer()\n                ),\n            ],\n            [\n                'callable()',\n                new Callable_(),\n            ],\n            [\n                'callable(): Foo',\n                new Callable_('callable', [], new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                'Closure(): Foo',\n                new Callable_('Closure', [], new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                '\\Closure(): Foo',\n                new Callable_('\\Closure', [], new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))),\n            ],\n            [\n                'callable(): (Foo&Bar)',\n                new Callable_(\n                    'callable',\n                    [],\n                    new Intersection(\n                        [\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')),\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\Bar')),\n                        ]\n                    )\n                ),\n            ],\n            [\n                'callable(A&...$a=, B&...=, C): Foo',\n                new Callable_(\n                    'callable',\n                    [\n                        new CallableParameter(\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\A')),\n                            'a',\n                            true,\n                            true,\n                            true\n                        ),\n                        new CallableParameter(\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\B')),\n                            null,\n                            true,\n                            true,\n                            true\n                        ),\n                        new CallableParameter(\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\C')),\n                            null,\n                            false,\n                            false,\n                            false\n                        ),\n                    ],\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))\n                ),\n            ],\n            [\n                'Closure(mixed): (callable(mixed): mixed)',\n                new Callable_(\n                    'Closure',\n                    [\n                        new CallableParameter(\n                            new Mixed_(),\n                            null,\n                            false,\n                            false,\n                            false\n                        ),\n                    ],\n                    new Callable_(\n                        'callable',\n                        [\n                            new CallableParameter(\n                                new Mixed_(),\n                                null,\n                                false,\n                                false,\n                                false\n                            ),\n                        ],\n                        new Mixed_()\n                    )\n                ),\n            ],\n        ];\n    }\n\n    /**\n     * @return array<array{0: string, 1: Type}>\n     */\n    public function constExpressions(): array\n    {\n        return [\n            [\n                '123',\n                new IntegerValue(123),\n            ],\n            [\n                'true',\n                new True_(),\n            ],\n            [\n                '123.2',\n                new FloatValue(123.2),\n            ],\n            [\n                '\"bar\"',\n                new StringValue('bar'),\n            ],\n            [\n                'Foo::FOO_CONSTANT',\n                new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')), 'FOO_CONSTANT'),\n            ],\n            [\n                'Foo::FOO_*',\n                new ConstExpression(new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo')), 'FOO_*'),\n            ],\n            [\n                'self::*|null',\n                new Compound([new ConstExpression(new Self_(), '*'), new Null_()]),\n            ],\n        ];\n    }\n\n    /**\n     * @return array<array{0: string, 1: Type}>\n     */\n    public function shapeStructures(): array\n    {\n        return [\n            [\n                'array{foo: string, bar: int}',\n                new ArrayShape(\n                    new ArrayShapeItem('foo', new String_(), false),\n                    new ArrayShapeItem('bar', new Integer(), false)\n                ),\n            ],\n            [\n                'array{string, int}',\n                new ArrayShape(\n                    new ArrayShapeItem(null, new String_(), false),\n                    new ArrayShapeItem(null, new Integer(), false)\n                ),\n            ],\n            [\n                'array{foo?: string, bar: int}',\n                new ArrayShape(\n                    new ArrayShapeItem('foo', new String_(), true),\n                    new ArrayShapeItem('bar', new Integer(), false)\n                ),\n            ],\n            [\n                'object{foo: string, bar: int}',\n                new ObjectShape(\n                    new ObjectShapeItem('foo', new String_(), false),\n                    new ObjectShapeItem('bar', new Integer(), false)\n                ),\n            ],\n            [\n                'list{1}',\n                new ListShape(\n                    new ListShapeItem(null, new IntegerValue(1), false)\n                ),\n            ],\n        ];\n    }\n\n    /**\n     * @return array<array{0: string, 1: Type}>\n     */\n    public function illegalLegacyFormatProvider(): array\n    {\n        return [\n            [\n                '?string |bool',\n                new Nullable(new String_()),\n            ],\n            [\n                '?string|?bool',\n                new Nullable(new String_()),\n            ],\n            [\n                '?string|?bool|null',\n                new Nullable(new String_()),\n            ],\n            [\n                '?string|bool|Foo',\n                new Nullable(new String_()),\n            ],\n            [\n                '?string&bool',\n                new Nullable(new String_()),\n            ],\n            [\n                '?string&bool|Foo',\n                new Nullable(new String_()),\n            ],\n            [\n                '?string&?bool|null',\n                new Nullable(new String_()),\n            ],\n        ];\n    }\n\n    public function testCreateTypeFromNull(): void\n    {\n        $fixture = new TypeResolver();\n\n        $this->assertEquals(new Mixed_(), $fixture->createType(null, new Context('')));\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/ArrayTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ArrayShapeItem;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ObjectShape;\nuse phpDocumentor\\Reflection\\PseudoTypes\\ObjectShapeItem;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ArrayTest extends TestCase\n{\n    public function testCreateWithoutParams(): void\n    {\n        $type = new Array_();\n\n        $this->assertNull($type->getOriginalKeyType());\n        $this->assertNull($type->getOriginalValueType());\n        $this->assertEquals(new Compound([new String_(), new Integer()]), $type->getKeyType());\n        $this->assertEquals(new Mixed_(), $type->getValueType());\n    }\n\n    public function testCreateWithParams(): void\n    {\n        $valueType = new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo\\\\Bar'));\n        $keyType = new Compound(\n            [\n                new String_(),\n                new Integer(),\n            ]\n        );\n\n        $type = new Array_($valueType, $keyType);\n\n        $this->assertSame($keyType, $type->getOriginalKeyType());\n        $this->assertSame($valueType, $type->getOriginalValueType());\n        $this->assertSame($keyType, $type->getKeyType());\n        $this->assertSame($valueType, $type->getValueType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(Array_ $array, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $array);\n    }\n\n    /**\n     * @return array<string, array{Array_, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'simple array' => [new Array_(), 'array'],\n            'array of mixed' => [new Array_(new Mixed_()), 'mixed[]'],\n            'array of single type' => [new Array_(new String_()), 'string[]'],\n            'multidimensional array' => [new Array_(new Array_(new String_())), 'string[][]'],\n            'array of compound type' => [new Array_(new Compound([new Integer(), new String_()])), 'array<int|string>'],\n            'array with key type' => [new Array_(new String_(), new Integer()), 'array<int, string>'],\n            'array of array shapes' => [\n                new Array_(\n                    new ArrayShape(\n                        new ArrayShapeItem('foo', new String_(), false),\n                        new ArrayShapeItem('bar', new Integer(), false)\n                    )\n                ),\n                'array<array{foo: string, bar: int}>',\n            ],\n            'array of object shapes' => [\n                new Array_(\n                    new ObjectShape(\n                        new ObjectShapeItem('foo', new String_(), false),\n                        new ObjectShapeItem('bar', new Integer(), false)\n                    )\n                ),\n                'array<object{foo: string, bar: int}>',\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/BooleanTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class BooleanTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new Boolean();\n\n        $this->assertSame('bool', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/CallableParameterTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class CallableParameterTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $type = new Object_(new Fqsen('\\\\phpDocumentor\\\\A'));\n        $name = 'param';\n        $parameter = new CallableParameter(\n            $type,\n            $name,\n            true,\n            true,\n            true\n        );\n\n        $this->assertSame($type, $parameter->getType());\n        $this->assertSame($name, $parameter->getName());\n        $this->assertTrue($parameter->isOptional());\n        $this->assertTrue($parameter->isReference());\n        $this->assertTrue($parameter->isVariadic());\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/CallableTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class CallableTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $identifier = 'callable';\n        $parameters = [\n            new CallableParameter(\n                new Object_(new Fqsen('\\\\phpDocumentor\\\\A')),\n                'a',\n                true,\n                true,\n                true\n            ),\n            new CallableParameter(\n                new Object_(new Fqsen('\\\\phpDocumentor\\\\B')),\n                null,\n                true,\n                true,\n                true\n            ),\n            new CallableParameter(\n                new Object_(new Fqsen('\\\\phpDocumentor\\\\C')),\n                null,\n                false,\n                false,\n                false\n            ),\n        ];\n\n        $returnType = new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'));\n\n        $type = new Callable_($identifier, $parameters, $returnType);\n\n        $this->assertSame($identifier, $type->getIdentifier());\n        $this->assertSame($parameters, $type->getParameters());\n        $this->assertSame($returnType, $type->getReturnType());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, Callable_ $type): void\n    {\n        $this->assertSame($expectedResult, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{string, Callable_}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'basic' => [\n                'callable',\n                new Callable_(),\n            ],\n            'pure' => [\n                'pure-callable(int): int',\n                new Callable_(\n                    'pure-callable',\n                    [new CallableParameter(new Integer())],\n                    new Integer()\n                ),\n            ],\n            'closure' => [\n                '\\Closure',\n                new Callable_('\\Closure'),\n            ],\n            'with different types' => [\n                'callable(\\\\phpDocumentor\\\\C, \\\\phpDocumentor\\\\A &...$a=, \\\\phpDocumentor\\\\B &...=): '\n                    . '\\\\phpDocumentor\\\\Foo',\n                new Callable_(\n                    'callable',\n                    [\n                        new CallableParameter(\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\C')),\n                            null,\n                            false,\n                            false,\n                            false\n                        ),\n                        new CallableParameter(\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\A')),\n                            'a',\n                            true,\n                            true,\n                            true\n                        ),\n                        new CallableParameter(\n                            new Object_(new Fqsen('\\\\phpDocumentor\\\\B')),\n                            null,\n                            true,\n                            true,\n                            true\n                        ),\n                    ],\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\Foo'))\n                ),\n            ],\n            'return callable' => [\n                'Closure(mixed): (callable(mixed): mixed)',\n                new Callable_(\n                    'Closure',\n                    [\n                        new CallableParameter(\n                            new Mixed_(),\n                            null,\n                            false,\n                            false,\n                            false\n                        ),\n                    ],\n                    new Callable_(\n                        'callable',\n                        [\n                            new CallableParameter(\n                                new Mixed_(),\n                                null,\n                                false,\n                                false,\n                                false\n                            ),\n                        ],\n                        new Mixed_()\n                    )\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/CompoundTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\nuse TypeError;\n\nuse function iterator_to_array;\n\nfinal class CompoundTest extends TestCase\n{\n    public function testCompoundCannotBeConstructedFromType(): void\n    {\n        $this->expectException(TypeError::class);\n        new Compound(['foo']);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::__construct\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::has\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     */\n    public function testCompoundGetType(): void\n    {\n        $integer = new Integer();\n\n        $this->assertSame($integer, (new Compound([$integer]))->get(0));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::__construct\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::has\n     */\n    public function testCompoundGetNotExistingType(): void\n    {\n        $this->assertNull((new Compound([]))->get(0));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::__construct\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     */\n    public function testCompoundHasIndex(): void\n    {\n        $this->assertTrue((new Compound([new Integer()]))->has(0));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::__construct\n     */\n    public function testCompoundDoesNotHasIndex(): void\n    {\n        $this->assertFalse((new Compound([]))->has(0));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::__construct\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     */\n    public function testCompoundContainsType(): void\n    {\n        $this->assertTrue((new Compound([new Integer()]))->contains(new Integer()));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::__construct\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     * @uses \\phpDocumentor\\Reflection\\Types\\String_\n     */\n    public function testCompoundDoesNotContainType(): void\n    {\n        $this->assertFalse((new Compound([new Integer()]))->contains(new String_()));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     * @uses \\phpDocumentor\\Reflection\\Types\\Boolean\n     */\n    public function testToString(): void\n    {\n        $this->assertSame('int|bool', (string) (new Compound([new Integer(), new Boolean()])));\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     * @uses \\phpDocumentor\\Reflection\\Types\\Boolean\n     */\n    public function testCompoundDoesNotContainDuplicates(): void\n    {\n        $compound = new Compound([new Integer(), new Integer(), new Boolean()]);\n\n        $this->assertCount(2, iterator_to_array($compound));\n        $this->assertSame('int|bool', (string) $compound);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Compound::__construct\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     * @uses \\phpDocumentor\\Reflection\\Types\\Boolean\n     */\n    public function testCompoundCanBeIterated(): void\n    {\n        $types = [new Integer(), new Boolean()];\n\n        foreach (new Compound($types) as $index => $type) {\n            $this->assertSame($types[$index], $type);\n        }\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     * @uses \\phpDocumentor\\Reflection\\Types\\Boolean\n     */\n    public function testCompoundIsMergedWithCompound(): void\n    {\n        $compound = new Compound([new Integer(), new Compound([new Integer(), new Boolean()])]);\n\n        $this->assertCount(2, iterator_to_array($compound));\n        $this->assertSame('int|bool', (string) $compound);\n    }\n\n    /**\n     * @uses \\phpDocumentor\\Reflection\\Types\\Integer\n     * @uses \\phpDocumentor\\Reflection\\Types\\Boolean\n     */\n    public function testCompoundIsMergedOnMergedWithIntersection(): void\n    {\n        $compound = new Compound([new Integer(), new Intersection([new Integer(), new Boolean()])]);\n\n        $this->assertCount(2, iterator_to_array($compound));\n        $this->assertSame('int|int&bool', (string) $compound);\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/ContextFactoryTest.php",
    "content": "<?php declare(strict_types=1);\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types {\n\n// Added imports on purpose as mock for the unit tests, please do not remove.\n    use phpDocumentor\\Reflection\\Fqsen as m, phpDocumentor;\n    use phpDocumentor\\Reflection\\DocBlock;\n    use phpDocumentor\\Reflection\\DocBlock\\Tag;\n    use PHPUnit\\Framework\\TestCase; // yes, the slash is part of the test\n    use PHPUnit\\Framework\\{\n        Assert,\n        Exception as e\n    };\n    use \\ReflectionClass;\n    use ReflectionClassConstant;\n    use ReflectionMethod;\n    use ReflectionParameter;\n    use ReflectionProperty;\n    use stdClass;\n\n    class ContextFactoryTest extends TestCase\n    {\n        public const TEST_CONSTANT = '';\n\n        public string $testProperty = '';\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testCreateFromClassReflection() : void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createFromReflector(new ReflectionClass($this));\n\n            $this->assertSame(__NAMESPACE__, $context->getNamespace());\n            $this->assertNamespaceAliasesFrom($context);\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testCreateFromMethodReflection() : void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createFromReflector(new ReflectionMethod($this, 'testCreateFromMethodReflection'));\n\n            $this->assertSame(__NAMESPACE__, $context->getNamespace());\n            $this->assertNamespaceAliasesFrom($context);\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testCreateFromPropertyReflection() : void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createFromReflector(new ReflectionProperty($this, 'testProperty'));\n\n            $this->assertSame(__NAMESPACE__, $context->getNamespace());\n            $this->assertNamespaceAliasesFrom($context);\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testCreateFromClassConstantReflection() : void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createFromReflector(new ReflectionClassConstant($this, 'TEST_CONSTANT'));\n\n            $this->assertSame(__NAMESPACE__, $context->getNamespace());\n            $this->assertNamespaceAliasesFrom($context);\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testCreateFromParameterReflection(): void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createFromReflector(new ReflectionParameter(fn($param) => $param, 'param'));\n\n            $this->assertSame(__NAMESPACE__, $context->getNamespace());\n            $this->assertNamespaceAliasesFrom($context);\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testReadsNamespaceFromProvidedNamespaceAndContent() : void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createForNamespace(__NAMESPACE__, (string) file_get_contents(__FILE__));\n\n            $this->assertSame(__NAMESPACE__, $context->getNamespace());\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testReadsAliasesFromProvidedNamespaceAndContent() : void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createForNamespace(__NAMESPACE__, (string) file_get_contents(__FILE__));\n\n            $this->assertNamespaceAliasesFrom($context);\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testTraitUseIsNotDetectedAsNamespaceUse() : void\n        {\n            $php = '<?php declare(strict_types=1);\n                namespace Foo;\n\n                trait FooTrait {}\n\n                class FooClass {\n                    use FooTrait;\n                }\n            ';\n\n            $fixture = new ContextFactory();\n            $context = $fixture->createForNamespace('Foo', $php);\n\n            $this->assertSame([], $context->getNamespaceAliases());\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testAllOpeningBracesAreCheckedWhenSearchingForEndOfClass() : void\n        {\n            $php = '<?php declare(strict_types=1);\n                namespace Foo;\n\n                trait FooTrait {}\n                trait BarTrait {}\n\n                class FooClass {\n                    use FooTrait;\n\n                    public function bar()\n                    {\n                        echo \"{$baz}\";\n                        echo \"${baz}\";\n                    }\n                }\n\n                class BarClass {\n                    use BarTrait;\n\n                    public function bar()\n                    {\n                        echo \"{$baz}\";\n                        echo \"${baz}\";\n                    }\n                }\n            ';\n\n            $fixture = new ContextFactory();\n            $context = $fixture->createForNamespace('Foo', $php);\n\n            $this->assertSame([], $context->getNamespaceAliases());\n        }\n\n        /**\n         * @uses phpDocumentor\\Reflection\\Types\\Context\n         */\n        public function testTraitContainsClosureWithUseStatement() : void\n        {\n            $php = '<?php declare(strict_types=1);\n                namespace Foo;\n\n                trait FooTrait {\n                    protected function check(array $data, string $key) : void\n                    {\n                        array_walk($data, function(&$item) use ($key) {\n                            // update item based on the key\n                        });\n                    }\n                }\n\n                class FooClass {\n                    use FooTrait;\n                }\n            ';\n\n            $fixture = new ContextFactory();\n            $context = $fixture->createForNamespace('Foo', $php);\n\n            $this->assertSame([], $context->getNamespaceAliases());\n        }\n\n        public function testEmptyFileName() : void\n        {\n            $fixture = new ContextFactory();\n            $context = $fixture->createFromReflector(new ReflectionClass(stdClass::class));\n\n            $this->assertSame([], $context->getNamespaceAliases());\n        }\n\n        public function testEvalDClass() : void\n        {\n            eval(<<<PHP\nnamespace Foo;\n\nfinal class Bar\n{\n}\nPHP\n);\n            $fixture = new ContextFactory();\n            $context = $fixture->createFromReflector(new ReflectionClass('Foo\\Bar'));\n\n            $this->assertSame([], $context->getNamespaceAliases());\n        }\n\n        public function assertNamespaceAliasesFrom(Context $context): void\n        {\n            $expected = [\n                'm' => m::class,\n                'DocBlock' => DocBlock::class,\n                'Tag' => Tag::class,\n                'phpDocumentor' => 'phpDocumentor',\n                'TestCase' => TestCase::class,\n                'Assert' => Assert::class,\n                'e' => e::class,\n                ReflectionClass::class => ReflectionClass::class,\n                ReflectionMethod::class => ReflectionMethod::class,\n                ReflectionProperty::class => ReflectionProperty::class,\n                ReflectionClassConstant::class => ReflectionClassConstant::class,\n                ReflectionParameter::class => ReflectionParameter::class,\n                \\stdClass::class => \\stdClass::class,\n            ];\n\n            $actual = $context->getNamespaceAliases();\n\n            // sort so that order differences don't break it\n            asort($expected);\n            asort($actual);\n\n            $this->assertSame($expected, $actual);\n        }\n    }\n}\n\nnamespace phpDocumentor\\Reflection\\Types\\Mock {\n\n    // the following import should not show in the tests above\n    use phpDocumentor\\Reflection\\Types\\AbstractList;\n\n    class Foo extends AbstractList\n    {\n        // dummy class\n\n        public function __toString(): string\n        {\n            return '';\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/ContextTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ContextTest extends TestCase\n{\n    public function testProvidesANormalizedNamespace(): void\n    {\n        $fixture = new Context('\\My\\Space');\n        $this->assertSame('My\\Space', $fixture->getNamespace());\n    }\n\n    public function testInterpretsNamespaceNamedGlobalAsRootNamespace(): void\n    {\n        $fixture = new Context('global');\n        $this->assertSame('', $fixture->getNamespace());\n    }\n\n    public function testInterpretsNamespaceNamedDefaultAsRootNamespace(): void\n    {\n        $fixture = new Context('default');\n        $this->assertSame('', $fixture->getNamespace());\n    }\n\n    public function testProvidesNormalizedNamespaceAliases(): void\n    {\n        $fixture = new Context('', ['Space' => '\\My\\Space']);\n        $this->assertSame(['Space' => 'My\\Space'], $fixture->getNamespaceAliases());\n\n        $fixture = new Context('', ['Space' => '\\My\\Space\\\\']);\n        $this->assertSame(['Space' => 'My\\Space'], $fixture->getNamespaceAliases());\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/IterableTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class IterableTest extends TestCase\n{\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(Iterable_ $iterable, string $expectedString): void\n    {\n        $this->assertSame($expectedString, (string) $iterable);\n    }\n\n    /**\n     * @return array<string, array{Iterable_, string}>\n     */\n    public function provideToStringData(): array\n    {\n        return [\n            'simple iterable' => [new Iterable_(), 'iterable'],\n            'iterable of mixed' => [new Iterable_(new Mixed_()), 'iterable<mixed>'],\n            'iterable of single type' => [new Iterable_(new String_()), 'iterable<string>'],\n            'iterable of compound type' => [\n                new Iterable_(new Compound([new Integer(), new String_()])),\n                'iterable<int|string>',\n            ],\n            'iterable with key type' => [new Iterable_(new String_(), new Integer()), 'iterable<int, string>'],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/NeverTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NeverTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new Never_();\n\n        $this->assertSame('never', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/NullableTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class NullableTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $realType = new String_();\n\n        $nullableString = new Nullable($realType);\n\n        $this->assertSame($realType, $nullableString->getActualType());\n    }\n\n    public function testToString(): void\n    {\n        $this->assertSame('?string', (string) new Nullable(new String_()));\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/ObjectTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse InvalidArgumentException;\nuse phpDocumentor\\Reflection\\Fqsen;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ObjectTest extends TestCase\n{\n    public function testCreateWithInvalidFqsen(): void\n    {\n        $fqsen = new Fqsen('\\\\Foo::BAR');\n\n        $this->expectExceptionObject(\n            new InvalidArgumentException(\n                'Object types can only refer to a class, interface or trait but a method, function, constant or '\n                . 'property was received: ' . (string) $fqsen\n            )\n        );\n\n        new Object_($fqsen);\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/ParentTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ParentTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new Parent_();\n\n        $this->assertSame('parent', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/ResourceTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ResourceTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new Resource_();\n\n        $this->assertSame('resource', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/SelfTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class SelfTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $genericTypes = [\n            new Object_(new Fqsen('\\\\phpDocumentor\\\\FirstClass')),\n            new Object_(new Fqsen('\\\\phpDocumentor\\\\SecondClass')),\n            new Object_(new Fqsen('\\\\phpDocumentor\\\\ThirdClass')),\n        ];\n\n        $type = new Self_(...$genericTypes);\n\n        $this->assertSame($genericTypes, $type->getGenericTypes());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, Self_ $type): void\n    {\n        $this->assertSame($expectedResult, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{string, Self_}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'basic' => [\n                'self',\n                new Self_(),\n            ],\n            'with generic' => [\n                'self<\\\\phpDocumentor\\\\FirstClass, \\\\phpDocumentor\\\\SecondClass, \\\\phpDocumentor\\\\ThirdClass>',\n                new Self_(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\FirstClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\SecondClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\ThirdClass')),\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/StaticTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse phpDocumentor\\Reflection\\Fqsen;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class StaticTest extends TestCase\n{\n    public function testCreate(): void\n    {\n        $genericTypes = [\n            new Object_(new Fqsen('\\\\phpDocumentor\\\\FirstClass')),\n            new Object_(new Fqsen('\\\\phpDocumentor\\\\SecondClass')),\n            new Object_(new Fqsen('\\\\phpDocumentor\\\\ThirdClass')),\n        ];\n\n        $type = new Static_(...$genericTypes);\n\n        $this->assertSame($genericTypes, $type->getGenericTypes());\n    }\n\n    /**\n     * @dataProvider provideToStringData\n     */\n    public function testToString(string $expectedResult, Static_ $type): void\n    {\n        $this->assertSame($expectedResult, (string) $type);\n    }\n\n    /**\n     * @return array<string, array{string, Static_}>\n     */\n    public static function provideToStringData(): array\n    {\n        return [\n            'basic' => [\n                'static',\n                new Static_(),\n            ],\n            'with generic' => [\n                'static<\\\\phpDocumentor\\\\FirstClass, \\\\phpDocumentor\\\\SecondClass, \\\\phpDocumentor\\\\ThirdClass>',\n                new Static_(\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\FirstClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\SecondClass')),\n                    new Object_(new Fqsen('\\\\phpDocumentor\\\\ThirdClass')),\n                ),\n            ],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/ThisTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ThisTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new This();\n\n        $this->assertSame('$this', (string) $type);\n    }\n}\n"
  },
  {
    "path": "tests/unit/Types/VoidTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\n/**\n * This file is part of phpDocumentor.\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n *\n * @link      http://phpdoc.org\n */\n\nnamespace phpDocumentor\\Reflection\\Types;\n\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class VoidTest extends TestCase\n{\n    public function testToString(): void\n    {\n        $type = new Void_();\n\n        $this->assertSame('void', (string) $type);\n    }\n}\n"
  }
]